summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java28
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java315
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java17
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java405
-rw-r--r--api/current.txt15
-rw-r--r--api/module-lib-current.txt1
-rw-r--r--api/system-current.txt19
-rw-r--r--api/test-current.txt27
-rw-r--r--core/java/android/app/Activity.java6
-rw-r--r--core/java/android/app/ActivityManager.java3
-rw-r--r--core/java/android/app/ActivityTaskManager.java6
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl1
-rw-r--r--core/java/android/app/INotificationManager.aidl4
-rw-r--r--core/java/android/app/NotificationManager.java14
-rw-r--r--core/java/android/app/SystemServiceRegistry.java10
-rw-r--r--core/java/android/app/TaskInfo.java7
-rw-r--r--core/java/android/bluetooth/le/ScanRecord.java3
-rw-r--r--core/java/android/companion/AssociationRequest.java29
-rw-r--r--core/java/android/content/ClipData.java105
-rw-r--r--core/java/android/content/ClipDescription.java29
-rw-r--r--core/java/android/content/ContentProvider.java14
-rw-r--r--core/java/android/content/ContentProviderNative.java29
-rw-r--r--core/java/android/content/ContentResolver.java9
-rw-r--r--core/java/android/content/Context.java11
-rw-r--r--core/java/android/content/IContentProvider.java9
-rw-r--r--core/java/android/content/Intent.java28
-rw-r--r--core/java/android/content/pm/PackageManager.java28
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java9
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java69
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java33
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl9
-rw-r--r--core/java/android/hardware/input/InputManager.java2
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java2
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java20
-rw-r--r--core/java/android/net/NetworkAgent.java5
-rw-r--r--core/java/android/net/TEST_MAPPING2
-rw-r--r--core/java/android/net/vcn/IVcnManagementService.aidl (renamed from packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl)13
-rw-r--r--core/java/android/net/vcn/VcnManager.java48
-rw-r--r--core/java/android/os/INetworkManagementService.aidl25
-rw-r--r--core/java/android/os/Process.java2
-rw-r--r--core/java/android/os/SharedMemory.java2
-rw-r--r--core/java/android/os/UserManager.java20
-rw-r--r--core/java/android/provider/DeviceConfig.java8
-rw-r--r--core/java/android/provider/Settings.java53
-rw-r--r--core/java/android/security/keystore/recovery/RecoveryController.java2
-rw-r--r--core/java/android/service/autofill/Dataset.java107
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java2
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java10
-rw-r--r--core/java/android/view/NotificationTopLineView.java80
-rw-r--r--core/java/android/view/SurfaceControl.java27
-rw-r--r--core/java/android/view/autofill/AutofillManager.java54
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl6
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java12
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java18
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java16
-rw-r--r--core/java/android/view/textclassifier/TextClassification.java3
-rw-r--r--core/java/android/window/DisplayAreaAppearedInfo.aidl (renamed from packages/Tethering/src/android/net/util/TetheringMessageBase.java)13
-rw-r--r--core/java/android/window/DisplayAreaAppearedInfo.java88
-rw-r--r--core/java/android/window/DisplayAreaOrganizer.java20
-rw-r--r--core/java/android/window/IDisplayAreaOrganizerController.aidl11
-rw-r--r--core/java/com/android/internal/app/SuspendedAppActivity.java2
-rw-r--r--core/java/com/android/internal/app/TEST_MAPPING8
-rw-r--r--core/java/com/android/internal/net/LegacyVpnInfo.java7
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java3
-rw-r--r--core/java/com/android/internal/protolog/common/ProtoLog.java40
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java23
-rw-r--r--core/jni/Android.bp30
-rw-r--r--core/jni/android_util_Binder.cpp1
-rw-r--r--core/jni/android_view_SurfaceControl.cpp20
-rw-r--r--core/proto/android/providers/settings/config.proto3
-rw-r--r--core/proto/android/providers/settings/global.proto2
-rw-r--r--core/res/res/layout/notification_template_material_base.xml4
-rw-r--r--core/res/res/layout/notification_template_top_line.xml9
-rw-r--r--core/res/res/values/attrs.xml4
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/BTtraffic/Android.bp7
-rw-r--r--core/tests/BTtraffic/AndroidManifest.xml22
-rw-r--r--core/tests/BTtraffic/README45
-rw-r--r--core/tests/BTtraffic/res/values/strings.xml3
-rw-r--r--core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java328
-rw-r--r--core/tests/SvcMonitor/Android.bp7
-rw-r--r--core/tests/SvcMonitor/AndroidManifest.xml21
-rw-r--r--core/tests/SvcMonitor/README27
-rw-r--r--core/tests/SvcMonitor/res/values/strings.xml3
-rw-r--r--core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java209
-rw-r--r--core/tests/coretests/src/android/content/ContentResolverTest.java8
-rw-r--r--core/tests/coretests/src/android/content/FakeProviderRemote.java9
-rw-r--r--data/fonts/fonts.xml95
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_controls.xml10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java67
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java119
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java278
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java177
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java34
-rw-r--r--libs/hostgraphics/Android.bp3
-rw-r--r--location/java/android/location/GnssMeasurement.java55
-rw-r--r--media/java/android/media/ApplicationMediaCapabilities.java153
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java94
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java1
-rw-r--r--non-updatable-api/current.txt14
-rw-r--r--non-updatable-api/module-lib-current.txt1
-rw-r--r--non-updatable-api/system-current.txt16
-rw-r--r--packages/CarSystemUI/res/values-ar/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-bn/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-de/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-eu/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-gl/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-gu/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-kn/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-ml/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-mr/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-ne/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-or/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-pa/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-te/strings.xml3
-rw-r--r--packages/CarSystemUI/res/values-ur/strings.xml3
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java7
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java2
-rwxr-xr-xpackages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java3
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java33
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java5
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java9
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java4
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml2
-rw-r--r--packages/SystemUI/res/values/attrs.xml2
-rw-r--r--packages/SystemUI/res/values/ids.xml1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java976
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java217
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java725
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java85
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java)244
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java)261
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java (renamed from packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java)2
-rw-r--r--packages/Tethering/Android.bp137
-rw-r--r--packages/Tethering/AndroidManifest.xml57
-rw-r--r--packages/Tethering/AndroidManifestBase.xml29
-rw-r--r--packages/Tethering/AndroidManifest_InProcess.xml34
-rw-r--r--packages/Tethering/OWNERS2
-rw-r--r--packages/Tethering/TEST_MAPPING12
-rw-r--r--packages/Tethering/apex/Android.bp48
-rw-r--r--packages/Tethering/apex/AndroidManifest.xml29
-rw-r--r--packages/Tethering/apex/com.android.tethering.avbpubkeybin1032 -> 0 bytes
-rw-r--r--packages/Tethering/apex/com.android.tethering.pem51
-rw-r--r--packages/Tethering/apex/com.android.tethering.pk8bin2375 -> 0 bytes
-rw-r--r--packages/Tethering/apex/com.android.tethering.x509.pem35
-rw-r--r--packages/Tethering/apex/manifest.json4
-rw-r--r--packages/Tethering/bpf_progs/Android.bp33
-rw-r--r--packages/Tethering/bpf_progs/offload.c207
-rw-r--r--packages/Tethering/common/TetheringLib/Android.bp47
-rw-r--r--packages/Tethering/common/TetheringLib/api/current.txt1
-rw-r--r--packages/Tethering/common/TetheringLib/api/module-lib-current.txt41
-rw-r--r--packages/Tethering/common/TetheringLib/api/module-lib-removed.txt1
-rw-r--r--packages/Tethering/common/TetheringLib/api/removed.txt1
-rw-r--r--packages/Tethering/common/TetheringLib/api/system-current.txt99
-rw-r--r--packages/Tethering/common/TetheringLib/api/system-removed.txt1
-rw-r--r--packages/Tethering/common/TetheringLib/jarjar-rules.txt1
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl25
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl52
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl39
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl31
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java239
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl35
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl37
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java93
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java1364
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl31
-rw-r--r--packages/Tethering/jarjar-rules.txt11
-rw-r--r--packages/Tethering/jni/android_net_util_TetheringUtils.cpp201
-rw-r--r--packages/Tethering/proguard.flags9
-rw-r--r--packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.pngbin891 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.pngbin963 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.pngbin909 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.pngbin534 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.pngbin554 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.pngbin518 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.pngbin675 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.pngbin659 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.pngbin698 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.pngbin1111 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.pngbin1274 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.pngbin1182 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.pngbin1046 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.pngbin1219 -> 0 bytes
-rw-r--r--packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.pngbin1002 -> 0 bytes
-rw-r--r--packages/Tethering/res/values-af/strings.xml29
-rw-r--r--packages/Tethering/res/values-am/strings.xml29
-rw-r--r--packages/Tethering/res/values-ar/strings.xml29
-rw-r--r--packages/Tethering/res/values-as/strings.xml29
-rw-r--r--packages/Tethering/res/values-az/strings.xml29
-rw-r--r--packages/Tethering/res/values-b+sr+Latn/strings.xml29
-rw-r--r--packages/Tethering/res/values-be/strings.xml29
-rw-r--r--packages/Tethering/res/values-bg/strings.xml29
-rw-r--r--packages/Tethering/res/values-bn/strings.xml29
-rw-r--r--packages/Tethering/res/values-bs/strings.xml29
-rw-r--r--packages/Tethering/res/values-ca/strings.xml29
-rw-r--r--packages/Tethering/res/values-cs/strings.xml29
-rw-r--r--packages/Tethering/res/values-da/strings.xml29
-rw-r--r--packages/Tethering/res/values-de/strings.xml29
-rw-r--r--packages/Tethering/res/values-el/strings.xml29
-rw-r--r--packages/Tethering/res/values-en-rAU/strings.xml29
-rw-r--r--packages/Tethering/res/values-en-rCA/strings.xml29
-rw-r--r--packages/Tethering/res/values-en-rGB/strings.xml29
-rw-r--r--packages/Tethering/res/values-en-rIN/strings.xml29
-rw-r--r--packages/Tethering/res/values-en-rXC/strings.xml29
-rw-r--r--packages/Tethering/res/values-es-rUS/strings.xml29
-rw-r--r--packages/Tethering/res/values-es/strings.xml29
-rw-r--r--packages/Tethering/res/values-et/strings.xml29
-rw-r--r--packages/Tethering/res/values-eu/strings.xml29
-rw-r--r--packages/Tethering/res/values-fa/strings.xml29
-rw-r--r--packages/Tethering/res/values-fi/strings.xml29
-rw-r--r--packages/Tethering/res/values-fr-rCA/strings.xml29
-rw-r--r--packages/Tethering/res/values-fr/strings.xml29
-rw-r--r--packages/Tethering/res/values-gl/strings.xml29
-rw-r--r--packages/Tethering/res/values-gu/strings.xml29
-rw-r--r--packages/Tethering/res/values-hi/strings.xml29
-rw-r--r--packages/Tethering/res/values-hr/strings.xml29
-rw-r--r--packages/Tethering/res/values-hu/strings.xml29
-rw-r--r--packages/Tethering/res/values-hy/strings.xml29
-rw-r--r--packages/Tethering/res/values-in/strings.xml29
-rw-r--r--packages/Tethering/res/values-is/strings.xml29
-rw-r--r--packages/Tethering/res/values-it/strings.xml29
-rw-r--r--packages/Tethering/res/values-iw/strings.xml29
-rw-r--r--packages/Tethering/res/values-ja/strings.xml29
-rw-r--r--packages/Tethering/res/values-ka/strings.xml29
-rw-r--r--packages/Tethering/res/values-kk/strings.xml29
-rw-r--r--packages/Tethering/res/values-km/strings.xml29
-rw-r--r--packages/Tethering/res/values-kn/strings.xml29
-rw-r--r--packages/Tethering/res/values-ko/strings.xml29
-rw-r--r--packages/Tethering/res/values-ky/strings.xml29
-rw-r--r--packages/Tethering/res/values-lo/strings.xml29
-rw-r--r--packages/Tethering/res/values-lt/strings.xml29
-rw-r--r--packages/Tethering/res/values-lv/strings.xml29
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-af/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-am/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-as/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-az/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-be/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-da/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-de/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-el/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-es/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-et/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-in/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-is/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-it/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-km/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-my/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-or/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-si/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-te/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-th/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml25
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-af/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-am/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-as/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-az/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-be/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-da/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-de/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-el/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-es/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-et/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-in/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-is/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-it/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-km/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-my/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-or/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-si/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-te/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-th/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004/config.xml23
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004/strings.xml28
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-af/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-am/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-as/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-az/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-be/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-da/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-de/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-el/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-es/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-et/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-in/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-is/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-it/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-km/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-my/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-or/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-si/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-te/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-th/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml24
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480/config.xml23
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480/strings.xml28
-rw-r--r--packages/Tethering/res/values-mk/strings.xml29
-rw-r--r--packages/Tethering/res/values-ml/strings.xml29
-rw-r--r--packages/Tethering/res/values-mn/strings.xml29
-rw-r--r--packages/Tethering/res/values-mr/strings.xml29
-rw-r--r--packages/Tethering/res/values-ms/strings.xml29
-rw-r--r--packages/Tethering/res/values-my/strings.xml29
-rw-r--r--packages/Tethering/res/values-nb/strings.xml29
-rw-r--r--packages/Tethering/res/values-ne/strings.xml29
-rw-r--r--packages/Tethering/res/values-nl/strings.xml29
-rw-r--r--packages/Tethering/res/values-or/strings.xml29
-rw-r--r--packages/Tethering/res/values-pa/strings.xml29
-rw-r--r--packages/Tethering/res/values-pl/strings.xml29
-rw-r--r--packages/Tethering/res/values-pt-rBR/strings.xml29
-rw-r--r--packages/Tethering/res/values-pt-rPT/strings.xml29
-rw-r--r--packages/Tethering/res/values-pt/strings.xml29
-rw-r--r--packages/Tethering/res/values-ro/strings.xml29
-rw-r--r--packages/Tethering/res/values-ru/strings.xml29
-rw-r--r--packages/Tethering/res/values-si/strings.xml29
-rw-r--r--packages/Tethering/res/values-sk/strings.xml29
-rw-r--r--packages/Tethering/res/values-sl/strings.xml29
-rw-r--r--packages/Tethering/res/values-sq/strings.xml29
-rw-r--r--packages/Tethering/res/values-sr/strings.xml29
-rw-r--r--packages/Tethering/res/values-sv/strings.xml29
-rw-r--r--packages/Tethering/res/values-sw/strings.xml29
-rw-r--r--packages/Tethering/res/values-ta/strings.xml29
-rw-r--r--packages/Tethering/res/values-te/strings.xml29
-rw-r--r--packages/Tethering/res/values-th/strings.xml29
-rw-r--r--packages/Tethering/res/values-tl/strings.xml29
-rw-r--r--packages/Tethering/res/values-tr/strings.xml29
-rw-r--r--packages/Tethering/res/values-uk/strings.xml29
-rw-r--r--packages/Tethering/res/values-ur/strings.xml29
-rw-r--r--packages/Tethering/res/values-uz/strings.xml29
-rw-r--r--packages/Tethering/res/values-vi/strings.xml29
-rw-r--r--packages/Tethering/res/values-zh-rCN/strings.xml29
-rw-r--r--packages/Tethering/res/values-zh-rHK/strings.xml29
-rw-r--r--packages/Tethering/res/values-zh-rTW/strings.xml29
-rw-r--r--packages/Tethering/res/values-zu/strings.xml29
-rw-r--r--packages/Tethering/res/values/config.xml194
-rw-r--r--packages/Tethering/res/values/overlayable.xml46
-rw-r--r--packages/Tethering/res/values/strings.xml47
-rw-r--r--packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java36
-rw-r--r--packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java203
-rw-r--r--packages/Tethering/src/android/net/ip/DadProxy.java54
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java1422
-rw-r--r--packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java180
-rw-r--r--packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java744
-rw-r--r--packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java75
-rw-r--r--packages/Tethering/src/android/net/util/InterfaceSet.java52
-rw-r--r--packages/Tethering/src/android/net/util/PrefixUtils.java88
-rw-r--r--packages/Tethering/src/android/net/util/TetheringUtils.java165
-rw-r--r--packages/Tethering/src/android/net/util/VersionedBroadcastListener.java106
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java778
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java183
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java646
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java328
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java811
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java574
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java416
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/Tethering.java2427
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java521
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java167
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java102
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java362
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java389
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java607
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java51
-rw-r--r--packages/Tethering/tests/Android.bp23
-rw-r--r--packages/Tethering/tests/integration/Android.bp85
-rw-r--r--packages/Tethering/tests/integration/AndroidManifest.xml28
-rw-r--r--packages/Tethering/tests/integration/AndroidManifest_coverage.xml29
-rw-r--r--packages/Tethering/tests/integration/AndroidTest_Coverage.xml12
-rw-r--r--packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java566
-rw-r--r--packages/Tethering/tests/jarjar-rules.txt19
-rw-r--r--packages/Tethering/tests/mts/Android.bp56
-rw-r--r--packages/Tethering/tests/mts/AndroidManifest.xml34
-rw-r--r--packages/Tethering/tests/mts/AndroidTest.xml36
-rw-r--r--packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java183
-rw-r--r--packages/Tethering/tests/privileged/Android.bp49
-rw-r--r--packages/Tethering/tests/privileged/AndroidManifest.xml32
-rw-r--r--packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java276
-rw-r--r--packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java130
-rw-r--r--packages/Tethering/tests/unit/Android.bp93
-rw-r--r--packages/Tethering/tests/unit/AndroidManifest.xml35
-rw-r--r--packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt122
-rw-r--r--packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java123
-rw-r--r--packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java1201
-rw-r--r--packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java61
-rw-r--r--packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java87
-rw-r--r--packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java131
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java607
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt162
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java623
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java156
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java72
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java827
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java264
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java551
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java458
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt444
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java484
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java2019
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java800
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java29
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java27
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java1
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java22
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java54
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java5
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java103
-rw-r--r--services/core/java/com/android/server/VibratorService.java50
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java20
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java20
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java6
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java6
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java175
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java1
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java118
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java1
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecConfig.java52
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java58
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java45
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiUtils.java93
-rw-r--r--services/core/java/com/android/server/hdmi/HotplugDetectionAction.java2
-rw-r--r--services/core/java/com/android/server/hdmi/NewDeviceAction.java4
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java5
-rw-r--r--services/core/java/com/android/server/location/LocationProviderManager.java21
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java3
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java74
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java66
-rw-r--r--services/core/java/com/android/server/notification/NotificationShellCmd.java6
-rw-r--r--services/core/java/com/android/server/notification/ZenLog.java13
-rw-r--r--services/core/java/com/android/server/notification/ZenModeConditions.java2
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java22
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java51
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java17
-rw-r--r--services/core/java/com/android/server/pm/Settings.java67
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java123
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermission.java253
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java20
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java182
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermissionState.java22
-rw-r--r--services/core/java/com/android/server/pm/permission/Permission.java (renamed from services/core/java/com/android/server/pm/permission/BasePermission.java)367
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java483
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java50
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionRegistry.java206
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionSettings.java277
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionState.java6
-rw-r--r--services/core/java/com/android/server/pm/permission/UidPermissionState.java10
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java8
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java15
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java74
-rw-r--r--services/core/java/com/android/server/wm/SurfaceFreezer.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java38
-rw-r--r--services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java12
-rw-r--r--services/core/jni/Android.bp3
-rw-r--r--services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp6
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java80
-rw-r--r--services/java/com/android/server/SystemServer.java20
-rw-r--r--services/net/Android.bp5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java206
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java80
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java87
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java1360
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java45
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorServiceTest.java77
-rw-r--r--services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java158
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java97
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java65
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java72
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java363
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java177
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java102
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java6
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java31
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java24
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthGsm.java3
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java3
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java48
-rw-r--r--test-mock/src/android/test/mock/MockContentProvider.java18
-rw-r--r--test-mock/src/android/test/mock/MockIContentProvider.java15
-rw-r--r--tests/net/java/android/net/ipmemorystore/ParcelableTests.java22
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java24
-rw-r--r--tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java5
-rw-r--r--tools/validatekeymaps/Android.bp1
-rw-r--r--wifi/Android.bp36
-rw-r--r--wifi/api/system-current.txt3
-rw-r--r--wifi/jarjar-rules.txt2
-rw-r--r--wifi/java/android/net/wifi/SoftApCapability.java5
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java10
-rw-r--r--wifi/java/android/net/wifi/SoftApInfo.java6
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java65
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java4
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSuggestion.java15
-rw-r--r--wifi/java/android/net/wifi/aware/Characteristics.java5
-rw-r--r--wifi/java/android/net/wifi/aware/WifiAwareManager.java9
-rw-r--r--wifi/java/android/net/wifi/util/SdkLevelUtil.java68
-rw-r--r--wifi/tests/Android.bp7
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java9
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java9
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApInfoTest.java9
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java5
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java13
-rw-r--r--wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java5
-rw-r--r--wifi/tests/test-jarjar-rules.txt1
796 files changed, 8908 insertions, 40240 deletions
diff --git a/Android.bp b/Android.bp
index b3dc98b2fa06..6b55cc9f5ea8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -633,6 +633,7 @@ java_library {
"//frameworks/base/apex/blobstore/framework",
"//frameworks/base/apex/jobscheduler/framework",
"//frameworks/base/packages/Tethering/tests/unit",
+ "//packages/modules/Connectivity/Tethering/tests/unit",
],
errorprone: {
javacflags: [
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 04feef485048..a8c0f0ec3e00 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -31,6 +31,8 @@ import android.util.IndentingPrintWriter;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -39,17 +41,23 @@ import java.util.Date;
* expires. The timer will wake up the device if the alarm is a "wakeup" alarm.
*/
class Alarm {
- private static final int NUM_POLICIES = 2;
+ @VisibleForTesting
+ public static final int NUM_POLICIES = 3;
/**
* Index used to store the time the alarm was requested to expire. To be used with
- * {@link #setPolicyElapsed(int, long)}
+ * {@link #setPolicyElapsed(int, long)}.
*/
public static final int REQUESTER_POLICY_INDEX = 0;
/**
* Index used to store the earliest time the alarm can expire based on app-standby policy.
- * To be used with {@link #setPolicyElapsed(int, long)}
+ * To be used with {@link #setPolicyElapsed(int, long)}.
*/
public static final int APP_STANDBY_POLICY_INDEX = 1;
+ /**
+ * Index used to store the earliest time the alarm can expire based on the device's doze policy.
+ * To be used with {@link #setPolicyElapsed(int, long)}.
+ */
+ public static final int DEVICE_IDLE_POLICY_INDEX = 2;
public final int type;
/**
@@ -128,11 +136,19 @@ class Alarm {
* @param policyIndex The index of the policy. One of [{@link #REQUESTER_POLICY_INDEX},
* {@link #APP_STANDBY_POLICY_INDEX}].
*/
- public long getPolicyElapsed(int policyIndex) {
+ @VisibleForTesting
+ long getPolicyElapsed(int policyIndex) {
return mPolicyWhenElapsed[policyIndex];
}
/**
+ * @return the time this alarm was requested to go off in the elapsed time base.
+ */
+ public long getRequestedElapsed() {
+ return mPolicyWhenElapsed[REQUESTER_POLICY_INDEX];
+ }
+
+ /**
* Get the earliest time that this alarm should be delivered to the requesting app.
*/
public long getWhenElapsed() {
@@ -205,8 +221,10 @@ class Alarm {
return "requester";
case APP_STANDBY_POLICY_INDEX:
return "app_standby";
+ case DEVICE_IDLE_POLICY_INDEX:
+ return "device_idle";
default:
- return "unknown";
+ 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 f196567bc391..c8a04d674739 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -21,12 +21,14 @@ import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
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.DEVICE_IDLE_POLICY_INDEX;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
import android.annotation.NonNull;
@@ -81,7 +83,6 @@ import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.LongArrayQueue;
-import android.util.MutableBoolean;
import android.util.NtpTrustedTime;
import android.util.Pair;
import android.util.Slog;
@@ -699,36 +700,31 @@ public class AlarmManagerService extends SystemService {
final HashMap<String, PriorityClass> mPriorities = new HashMap<>();
int mCurrentSeq = 0;
- static final class WakeupEvent {
- public long when;
- public int uid;
- public String action;
-
- public WakeupEvent(long theTime, int theUid, String theAction) {
- when = theTime;
- uid = theUid;
- action = theAction;
- }
- }
-
final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() {
@Override
public int compare(Alarm lhs, Alarm rhs) {
- // priority class trumps everything. TICK < WAKEUP < NORMAL
+
+ // Alarm to exit device_idle should go out first.
+ final boolean lhsIdleUntil = (lhs.flags & FLAG_IDLE_UNTIL) != 0;
+ final boolean rhsIdleUntil = (rhs.flags & FLAG_IDLE_UNTIL) != 0;
+ if (lhsIdleUntil != rhsIdleUntil) {
+ return lhsIdleUntil ? -1 : 1;
+ }
+
+ // Then, priority class trumps everything. TICK < WAKEUP < NORMAL
if (lhs.priorityClass.priority < rhs.priorityClass.priority) {
return -1;
} else if (lhs.priorityClass.priority > rhs.priorityClass.priority) {
return 1;
}
- // within each class, sort by nominal delivery time
- if (lhs.getWhenElapsed() < rhs.getWhenElapsed()) {
+ // within each class, sort by requested delivery time
+ if (lhs.getRequestedElapsed() < rhs.getRequestedElapsed()) {
return -1;
- } else if (lhs.getWhenElapsed() > rhs.getWhenElapsed()) {
+ } else if (lhs.getRequestedElapsed() > rhs.getRequestedElapsed()) {
return 1;
}
- // same priority class + same target delivery time
return 0;
}
};
@@ -777,10 +773,9 @@ public class AlarmManagerService extends SystemService {
final AlarmStore mAlarmStore;
// set to non-null if in idle mode; while in this mode, any alarms we don't want
- // to run during this time are placed in mPendingWhileIdleAlarms
+ // to run during this time are rescehduled to go off after this alarm.
Alarm mPendingIdleUntil = null;
Alarm mNextWakeFromIdle = null;
- ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>();
@VisibleForTesting
AlarmManagerService(Context context, Injector injector) {
@@ -830,15 +825,21 @@ public class AlarmManagerService extends SystemService {
return restoreRequestedTime(a);
});
- if (mNextWakeFromIdle != null && isRtc(mNextWakeFromIdle.type)) {
- // The next wake from idle got updated due to the rtc time change, implying we need
- // to update the time we have to come out of idle too.
- changed |= mAlarmStore.updateAlarmDeliveries(a -> {
- if (a != mPendingIdleUntil) {
- return false;
+ if (changed && mPendingIdleUntil != null) {
+ if (mNextWakeFromIdle != null && isRtc(mNextWakeFromIdle.type)) {
+ // The next wake from idle got updated due to the rtc time change, so we need
+ // to update the time we have to come out of idle too.
+ final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries(a -> {
+ if (a != mPendingIdleUntil) {
+ return false;
+ }
+ return adjustIdleUntilTime(a);
+ });
+ if (idleUntilUpdated) {
+ mAlarmStore.updateAlarmDeliveries(
+ alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
}
- return adjustIdleUntilTime(a);
- });
+ }
}
if (changed) {
@@ -972,11 +973,10 @@ public class AlarmManagerService extends SystemService {
// Recurring alarms may have passed several alarm intervals while the
// alarm was kept pending. Send the appropriate trigger count.
if (alarm.repeatInterval > 0) {
- alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX))
- / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.getRequestedElapsed()) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
- final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta;
+ final long nextElapsed = alarm.getRequestedElapsed() + delta;
final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed,
alarm.repeatInterval);
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
@@ -1015,25 +1015,6 @@ public class AlarmManagerService extends SystemService {
}
}
- void restorePendingWhileIdleAlarmsLocked() {
- if (RECORD_DEVICE_IDLE_ALARMS) {
- IdleDispatchEntry ent = new IdleDispatchEntry();
- ent.uid = 0;
- ent.pkg = "FINISH IDLE";
- ent.elapsedRealtime = mInjector.getElapsedRealtime();
- mAllowWhileIdleDispatches.add(ent);
- }
-
- // Bring pending alarms back into the main list.
- if (mPendingWhileIdleAlarms.size() > 0) {
- ArrayList<Alarm> alarms = mPendingWhileIdleAlarms;
- mPendingWhileIdleAlarms = new ArrayList<>();
- for (int i = alarms.size() - 1; i >= 0; i--) {
- setImplLocked(alarms.get(i));
- }
- }
- }
-
static final class InFlight {
final PendingIntent mPendingIntent;
final long mWhenElapsed;
@@ -1571,7 +1552,7 @@ public class AlarmManagerService extends SystemService {
return false;
}
restoreRequestedTime(alarm);
- long triggerBeforeFuzz = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX);
+ long triggerBeforeFuzz = alarm.getRequestedElapsed();
if (mNextWakeFromIdle != null && triggerBeforeFuzz > mNextWakeFromIdle.getWhenElapsed()) {
triggerBeforeFuzz = mNextWakeFromIdle.getWhenElapsed();
}
@@ -1591,6 +1572,35 @@ public class AlarmManagerService extends SystemService {
}
/**
+ * Adjusts the delivery time of the alarm based on device_idle (doze) rules.
+ *
+ * @param alarm The alarm to adjust
+ * @return {@code true} if the alarm delivery time was updated.
+ */
+ private boolean adjustDeliveryTimeBasedOnDeviceIdle(Alarm alarm) {
+ final long nowElapsed = mInjector.getElapsedRealtime();
+ if (mPendingIdleUntil == null || mPendingIdleUntil == alarm) {
+ return alarm.setPolicyElapsed(DEVICE_IDLE_POLICY_INDEX, nowElapsed);
+ }
+
+ final long deviceIdlePolicyTime;
+ if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
+ | AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0) {
+ // Unrestricted.
+ deviceIdlePolicyTime = nowElapsed;
+ } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ // Allowed but limited.
+ final long lastDispatch = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0);
+ deviceIdlePolicyTime = (lastDispatch == 0) ? nowElapsed
+ : lastDispatch + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+ } else {
+ // Not allowed.
+ deviceIdlePolicyTime = mPendingIdleUntil.getWhenElapsed();
+ }
+ return alarm.setPolicyElapsed(DEVICE_IDLE_POLICY_INDEX, deviceIdlePolicyTime);
+ }
+
+ /**
* Adjusts the alarm's policy time for app_standby.
*
* @param alarm The alarm to update.
@@ -1643,12 +1653,6 @@ public class AlarmManagerService extends SystemService {
return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed);
}
- private static boolean isAllowedWhileIdle(Alarm a) {
- return ((a.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE
- | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
- | AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0);
- }
-
private void setImplLocked(Alarm a) {
if ((a.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
adjustIdleUntilTime(a);
@@ -1676,16 +1680,9 @@ public class AlarmManagerService extends SystemService {
mAlarmStore.remove(mPendingIdleUntil::equals);
}
mPendingIdleUntil = a;
- final ArrayList<Alarm> notAllowedWhileIdleAlarms = mAlarmStore.remove(
- alarm -> !isAllowedWhileIdle(alarm));
- mPendingWhileIdleAlarms.addAll(notAllowedWhileIdleAlarms);
+ mAlarmStore.updateAlarmDeliveries(alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
} else if (mPendingIdleUntil != null) {
- // We currently have an idle until alarm scheduled; if the new alarm has
- // not explicitly stated it wants to run while idle, then put it on hold.
- if (!isAllowedWhileIdle(a)) {
- mPendingWhileIdleAlarms.add(a);
- return;
- }
+ adjustDeliveryTimeBasedOnDeviceIdle(a);
}
if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed()
@@ -1694,12 +1691,17 @@ public class AlarmManagerService extends SystemService {
// If this wake from idle is earlier than whatever was previously scheduled,
// and we are currently idling, then the idle-until time needs to be updated.
if (mPendingIdleUntil != null) {
- mAlarmStore.updateAlarmDeliveries(alarm -> {
+ final boolean updated = mAlarmStore.updateAlarmDeliveries(alarm -> {
if (alarm != mPendingIdleUntil) {
return false;
}
return adjustIdleUntilTime(alarm);
});
+ if (updated) {
+ // idle-until got updated, so also update all alarms not allowed while idle.
+ mAlarmStore.updateAlarmDeliveries(
+ alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
+ }
}
}
}
@@ -2095,7 +2097,7 @@ public class AlarmManagerService extends SystemService {
mAppWakeupHistory.dump(pw, nowELAPSED);
- if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) {
+ if (mPendingIdleUntil != null) {
pw.println();
pw.println("Idle mode state:");
@@ -2107,8 +2109,6 @@ public class AlarmManagerService extends SystemService {
} else {
pw.println("null");
}
- pw.println("Pending alarms:");
- dumpAlarmList(pw, mPendingWhileIdleAlarms, nowELAPSED, sdf);
pw.decreaseIndent();
}
if (mNextWakeFromIdle != null) {
@@ -2428,10 +2428,6 @@ public class AlarmManagerService extends SystemService {
mPendingIdleUntil.dumpDebug(
proto, AlarmManagerServiceDumpProto.PENDING_IDLE_UNTIL, nowElapsed);
}
- for (Alarm a : mPendingWhileIdleAlarms) {
- a.dumpDebug(proto, AlarmManagerServiceDumpProto.PENDING_WHILE_IDLE_ALARMS,
- nowElapsed);
- }
if (mNextWakeFromIdle != null) {
mNextWakeFromIdle.dumpDebug(proto, AlarmManagerServiceDumpProto.NEXT_WAKE_FROM_IDLE,
nowElapsed);
@@ -2752,21 +2748,13 @@ public class AlarmManagerService extends SystemService {
return;
}
- final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(operation, directReceiver);
- final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms);
+ final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(
+ a -> a.matches(operation, directReceiver));
for (final Alarm removed : removedAlarms) {
decrementAlarmCount(removed.uid, 1);
}
final boolean didRemove = !removedAlarms.isEmpty();
- for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- final Alarm alarm = mPendingWhileIdleAlarms.get(i);
- if (alarm.matches(operation, directReceiver)) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- mPendingWhileIdleAlarms.remove(i);
- decrementAlarmCount(alarm.uid, 1);
- }
- }
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
@@ -2793,22 +2781,25 @@ public class AlarmManagerService extends SystemService {
if (DEBUG_BATCH) {
Slog.v(TAG, "remove(operation) changed bounds; rebatching");
}
- boolean restorePending = false;
+ boolean idleUntilUpdated = false;
if (mPendingIdleUntil != null && mPendingIdleUntil.matches(operation, directReceiver)) {
mPendingIdleUntil = null;
- restorePending = true;
+ idleUntilUpdated = true;
}
if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) {
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
- mAlarmStore.updateAlarmDeliveries(alarm -> {
- if (alarm != mPendingIdleUntil) {
- return false;
- }
- return adjustIdleUntilTime(alarm);
- });
+ if (mPendingIdleUntil != null) {
+ idleUntilUpdated |= mAlarmStore.updateAlarmDeliveries(alarm -> {
+ if (alarm != mPendingIdleUntil) {
+ return false;
+ }
+ return adjustIdleUntilTime(alarm);
+ });
+ }
}
- if (restorePending) {
- restorePendingWhileIdleAlarmsLocked();
+ if (idleUntilUpdated) {
+ mAlarmStore.updateAlarmDeliveries(
+ alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
}
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
@@ -2821,21 +2812,12 @@ public class AlarmManagerService extends SystemService {
return;
}
- final Predicate<Alarm> whichAlarms = (Alarm a) -> a.uid == uid;
- final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
+ final ArrayList<Alarm> removed = mAlarmStore.remove(a -> a.uid == uid);
final boolean didRemove = !removed.isEmpty();
if (didRemove) {
decrementAlarmCount(uid, removed.size());
}
- for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- final Alarm a = mPendingWhileIdleAlarms.get(i);
- if (a.uid == uid) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- mPendingWhileIdleAlarms.remove(i);
- decrementAlarmCount(uid, 1);
- }
- }
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
@@ -2850,20 +2832,26 @@ public class AlarmManagerService extends SystemService {
}
// If we're currently using this app's alarms to come out of doze,
// make sure to reset to any remaining WAKE_FROM_IDLE alarms.
+ boolean idleUntilUpdated = false;
if (mNextWakeFromIdle != null && mNextWakeFromIdle.uid == uid) {
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
- mAlarmStore.updateAlarmDeliveries(alarm -> {
- if (alarm != mPendingIdleUntil) {
- return false;
- }
- return adjustIdleUntilTime(alarm);
- });
+ if (mPendingIdleUntil != null) {
+ idleUntilUpdated |= mAlarmStore.updateAlarmDeliveries(alarm -> {
+ if (alarm != mPendingIdleUntil) {
+ return false;
+ }
+ return adjustIdleUntilTime(alarm);
+ });
+ }
}
if (mPendingIdleUntil != null && mPendingIdleUntil.uid == uid) {
// Should never happen - only the system uid is allowed to set idle-until alarms
Slog.wtf(TAG, "Removed app uid " + uid + " set idle-until alarm!");
mPendingIdleUntil = null;
- restorePendingWhileIdleAlarmsLocked();
+ idleUntilUpdated = true;
+ }
+ if (idleUntilUpdated) {
+ mAlarmStore.updateAlarmDeliveries(alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
}
if (didRemove) {
if (DEBUG_BATCH) {
@@ -2883,29 +2871,12 @@ public class AlarmManagerService extends SystemService {
return;
}
- final MutableBoolean removedNextWakeFromIdle = new MutableBoolean(false);
- final Predicate<Alarm> whichAlarms = (Alarm a) -> {
- final boolean didMatch = a.matches(packageName);
- if (didMatch && a == mNextWakeFromIdle) {
- removedNextWakeFromIdle.value = true;
- }
- return didMatch;
- };
-
- final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
+ final ArrayList<Alarm> removed = mAlarmStore.remove(a -> a.matches(packageName));
final boolean didRemove = !removed.isEmpty();
if (didRemove) {
decrementAlarmCount(removed.get(0).uid, removed.size());
}
- for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- final Alarm a = mPendingWhileIdleAlarms.get(i);
- if (a.matches(packageName)) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- mPendingWhileIdleAlarms.remove(i);
- decrementAlarmCount(a.uid, 1);
- }
- }
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
@@ -2921,14 +2892,20 @@ public class AlarmManagerService extends SystemService {
}
// If we're currently using this app's alarms to come out of doze,
// make sure to reset to any remaining WAKE_FROM_IDLE alarms.
- if (removedNextWakeFromIdle.value) {
+ if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(packageName)) {
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
- mAlarmStore.updateAlarmDeliveries(alarm -> {
- if (alarm != mPendingIdleUntil) {
- return false;
+ if (mPendingIdleUntil != null) {
+ final boolean updated = mAlarmStore.updateAlarmDeliveries(alarm -> {
+ if (alarm != mPendingIdleUntil) {
+ return false;
+ }
+ return adjustIdleUntilTime(alarm);
+ });
+ if (updated) {
+ mAlarmStore.updateAlarmDeliveries(
+ alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
}
- return adjustIdleUntilTime(alarm);
- });
+ }
}
if (didRemove) {
if (DEBUG_BATCH) {
@@ -2953,14 +2930,6 @@ public class AlarmManagerService extends SystemService {
decrementAlarmCount(uid, removed.size());
}
- for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- final Alarm a = mPendingWhileIdleAlarms.get(i);
- if (a.uid == uid) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- mPendingWhileIdleAlarms.remove(i);
- decrementAlarmCount(uid, 1);
- }
- }
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
if (mPendingBackgroundAlarms.keyAt(i) == uid) {
final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i);
@@ -2992,14 +2961,6 @@ public class AlarmManagerService extends SystemService {
}
final boolean didRemove = !removedAlarms.isEmpty();
- for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- if (UserHandle.getUserId(mPendingWhileIdleAlarms.get(i).creatorUid)
- == userHandle) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- final Alarm removed = mPendingWhileIdleAlarms.remove(i);
- decrementAlarmCount(removed.uid, 1);
- }
- }
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
if (UserHandle.getUserId(mPendingBackgroundAlarms.keyAt(i)) == userHandle) {
final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i);
@@ -3016,6 +2977,21 @@ public class AlarmManagerService extends SystemService {
mLastAllowWhileIdleDispatch.removeAt(i);
}
}
+ if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
+ mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+ if (mPendingIdleUntil != null) {
+ final boolean updated = mAlarmStore.updateAlarmDeliveries(alarm -> {
+ if (alarm != mPendingIdleUntil) {
+ return false;
+ }
+ return adjustIdleUntilTime(alarm);
+ });
+ if (updated) {
+ mAlarmStore.updateAlarmDeliveries(
+ alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
+ }
+ }
+ }
if (didRemove) {
if (DEBUG_BATCH) {
@@ -3062,12 +3038,6 @@ public class AlarmManagerService extends SystemService {
return true;
}
}
- for (int i = 0; i < mPendingWhileIdleAlarms.size(); i++) {
- final Alarm a = mPendingWhileIdleAlarms.get(i);
- if (a.matches(packageName)) {
- return true;
- }
- }
return false;
}
@@ -3134,16 +3104,10 @@ public class AlarmManagerService extends SystemService {
private static native long getNextAlarm(long nativeData, int type);
private long getWhileIdleMinIntervalLocked(int uid) {
- final boolean dozing = mPendingIdleUntil != null;
final boolean ebs = (mAppStateTracker != null)
&& mAppStateTracker.isForceAllAppsStandbyEnabled();
- if (!dozing && !ebs) {
- return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
- }
- if (dozing) {
- return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
- }
- if (mUseAllowWhileIdleShortTime.get(uid)) {
+
+ 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;
@@ -3201,16 +3165,12 @@ public class AlarmManagerService extends SystemService {
}
if (mPendingIdleUntil == alarm) {
mPendingIdleUntil = null;
- restorePendingWhileIdleAlarmsLocked();
+ mAlarmStore.updateAlarmDeliveries(a -> adjustDeliveryTimeBasedOnDeviceIdle(a));
}
if (mNextWakeFromIdle == alarm) {
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
- mAlarmStore.updateAlarmDeliveries(a -> {
- if (a != mPendingIdleUntil) {
- return false;
- }
- return adjustIdleUntilTime(a);
- });
+ // Note that we don't need to update mPendingIdleUntil because it should already
+ // be removed from the alarm store.
}
// Recurring alarms may have passed several alarm intervals while the
@@ -3218,11 +3178,10 @@ public class AlarmManagerService extends SystemService {
if (alarm.repeatInterval > 0) {
// this adjustment will be zero if we're late by
// less than one full repeat interval
- alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX))
- / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.getRequestedElapsed()) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
- final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta;
+ final long nextElapsed = alarm.getRequestedElapsed() + delta;
final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed,
alarm.repeatInterval);
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
@@ -4222,6 +4181,12 @@ 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);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index 227b8276abe1..56b97f2e5757 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -50,6 +50,9 @@ public final class TimeController extends StateController {
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
+ @VisibleForTesting
+ static final long DELAY_COALESCE_TIME_MS = 30_000L;
+
/** Deadline alarm tag for logging purposes */
private final String DEADLINE_TAG = "*job.deadline*";
/** Delay alarm tag for logging purposes */
@@ -57,6 +60,7 @@ public final class TimeController extends StateController {
private long mNextJobExpiredElapsedMillis;
private long mNextDelayExpiredElapsedMillis;
+ private volatile long mLastFiredDelayExpiredElapsedMillis;
private final boolean mChainedAttributionEnabled;
@@ -273,7 +277,6 @@ public final class TimeController extends StateController {
@VisibleForTesting
void checkExpiredDelaysAndResetAlarm() {
synchronized (mLock) {
- final long nowElapsedMillis = sElapsedRealtimeClock.millis();
long nextDelayTime = Long.MAX_VALUE;
int nextDelayUid = 0;
String nextDelayPackageName = null;
@@ -284,7 +287,7 @@ public final class TimeController extends StateController {
if (!job.hasTimingDelayConstraint()) {
continue;
}
- if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) {
+ if (evaluateTimingDelayConstraint(job, sElapsedRealtimeClock.millis())) {
if (canStopTrackingJobLocked(job)) {
it.remove();
}
@@ -356,7 +359,11 @@ public final class TimeController extends StateController {
* This alarm <b>will not</b> wake up the phone.
*/
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
- alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
+ // To avoid spamming AlarmManager in the case where many delay times are a few milliseconds
+ // apart, make sure the alarm is set no earlier than DELAY_COALESCE_TIME_MS since the last
+ // time a delay alarm went off and that the alarm is not scheduled for the past.
+ alarmTimeElapsedMillis = maybeAdjustAlarmTime(Math.max(alarmTimeElapsedMillis,
+ mLastFiredDelayExpiredElapsedMillis + DELAY_COALESCE_TIME_MS));
if (mNextDelayExpiredElapsedMillis == alarmTimeElapsedMillis) {
return;
}
@@ -416,6 +423,7 @@ public final class TimeController extends StateController {
if (DEBUG) {
Slog.d(TAG, "Delay-expired alarm fired");
}
+ mLastFiredDelayExpiredElapsedMillis = sElapsedRealtimeClock.millis();
checkExpiredDelaysAndResetAlarm();
}
};
@@ -429,6 +437,9 @@ public final class TimeController extends StateController {
pw.print("Next delay alarm in ");
TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw);
pw.println();
+ pw.print("Last delay alarm fired @ ");
+ TimeUtils.formatDuration(nowElapsed, mLastFiredDelayExpiredElapsedMillis, pw);
+ pw.println();
pw.print("Next deadline alarm in ");
TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw);
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 22e1eec8883d..6f7dde292c56 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -92,11 +92,11 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -117,8 +117,6 @@ import com.android.server.usage.AppIdleHistory.AppUsageHistory;
import java.io.File;
import java.io.PrintWriter;
-import java.time.Duration;
-import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -144,10 +142,11 @@ public class AppStandbyController implements AppStandbyInternal {
private static final long ONE_DAY = ONE_HOUR * 24;
/**
- * The minimum amount of time the screen must have been on before an app can time out from its
- * current bucket to the next bucket.
+ * The default minimum amount of time the screen must have been on before an app can time out
+ * from its current bucket to the next bucket.
*/
- private static final long[] SCREEN_TIME_THRESHOLDS = {
+ @VisibleForTesting
+ static final long[] DEFAULT_SCREEN_TIME_THRESHOLDS = {
0,
0,
COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR,
@@ -155,9 +154,10 @@ public class AppStandbyController implements AppStandbyInternal {
COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR
};
- /** The minimum allowed values for each index in {@link #SCREEN_TIME_THRESHOLDS}. */
- private static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME
- ? new long[SCREEN_TIME_THRESHOLDS.length]
+ /** The minimum allowed values for each index in {@link #DEFAULT_SCREEN_TIME_THRESHOLDS}. */
+ @VisibleForTesting
+ static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME
+ ? new long[DEFAULT_SCREEN_TIME_THRESHOLDS.length]
: new long[]{
0,
0,
@@ -167,20 +167,22 @@ public class AppStandbyController implements AppStandbyInternal {
};
/**
- * The minimum amount of elapsed time that must have passed before an app can time out from its
- * current bucket to the next bucket.
+ * The default minimum amount of elapsed time that must have passed before an app can time out
+ * from its current bucket to the next bucket.
*/
- private static final long[] ELAPSED_TIME_THRESHOLDS = {
+ @VisibleForTesting
+ static final long[] DEFAULT_ELAPSED_TIME_THRESHOLDS = {
0,
- COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
- COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
+ COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
+ COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
COMPRESS_TIME ? 32 * ONE_MINUTE : 30 * ONE_DAY
};
- /** The minimum allowed values for each index in {@link #ELAPSED_TIME_THRESHOLDS}. */
- private static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME
- ? new long[ELAPSED_TIME_THRESHOLDS.length]
+ /** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */
+ @VisibleForTesting
+ static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME
+ ? new long[DEFAULT_ELAPSED_TIME_THRESHOLDS.length]
: new long[]{
0,
ONE_HOUR,
@@ -198,7 +200,8 @@ public class AppStandbyController implements AppStandbyInternal {
};
/** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
- private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+ private static final long DEFAULT_PREDICTION_TIMEOUT =
+ COMPRESS_TIME ? 10 * ONE_MINUTE : 12 * ONE_HOUR;
/**
* Indicates the maximum wait time for admin data to be available;
@@ -269,58 +272,64 @@ public class AppStandbyController implements AppStandbyInternal {
static final int MSG_REPORT_SYNC_SCHEDULED = 12;
static final int MSG_REPORT_EXEMPTED_SYNC_START = 13;
- long mCheckIdleIntervalMillis;
+ long mCheckIdleIntervalMillis = Math.min(DEFAULT_ELAPSED_TIME_THRESHOLDS[1] / 4,
+ ConstantsObserver.DEFAULT_CHECK_IDLE_INTERVAL_MS);
/**
* The minimum amount of time the screen must have been on before an app can time out from its
* current bucket to the next bucket.
*/
- long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
+ long[] mAppStandbyScreenThresholds = DEFAULT_SCREEN_TIME_THRESHOLDS;
/**
* The minimum amount of elapsed time that must have passed before an app can time out from its
* current bucket to the next bucket.
*/
- long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
+ long[] mAppStandbyElapsedThresholds = DEFAULT_ELAPSED_TIME_THRESHOLDS;
/** Minimum time a strong usage event should keep the bucket elevated. */
- long mStrongUsageTimeoutMillis;
+ long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_STRONG_USAGE_TIMEOUT;
/** Minimum time a notification seen event should keep the bucket elevated. */
- long mNotificationSeenTimeoutMillis;
+ long mNotificationSeenTimeoutMillis = ConstantsObserver.DEFAULT_NOTIFICATION_TIMEOUT;
/** Minimum time a system update event should keep the buckets elevated. */
- long mSystemUpdateUsageTimeoutMillis;
+ long mSystemUpdateUsageTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_UPDATE_TIMEOUT;
/** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
- long mPredictionTimeoutMillis;
+ long mPredictionTimeoutMillis = DEFAULT_PREDICTION_TIMEOUT;
/** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */
- long mSyncAdapterTimeoutMillis;
+ long mSyncAdapterTimeoutMillis = ConstantsObserver.DEFAULT_SYNC_ADAPTER_TIMEOUT;
/**
* Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
* non-doze
*/
- long mExemptedSyncScheduledNonDozeTimeoutMillis;
+ long mExemptedSyncScheduledNonDozeTimeoutMillis =
+ ConstantsObserver.DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT;
/**
* Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
* doze
*/
- long mExemptedSyncScheduledDozeTimeoutMillis;
+ long mExemptedSyncScheduledDozeTimeoutMillis =
+ ConstantsObserver.DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT;
/**
* Maximum time an exempted sync should keep the buckets elevated, when sync is started.
*/
- long mExemptedSyncStartTimeoutMillis;
+ long mExemptedSyncStartTimeoutMillis = ConstantsObserver.DEFAULT_EXEMPTED_SYNC_START_TIMEOUT;
/**
* Maximum time an unexempted sync should keep the buckets elevated, when sync is scheduled
*/
- long mUnexemptedSyncScheduledTimeoutMillis;
+ long mUnexemptedSyncScheduledTimeoutMillis =
+ ConstantsObserver.DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT;
/** Maximum time a system interaction should keep the buckets elevated. */
- long mSystemInteractionTimeoutMillis;
+ long mSystemInteractionTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_INTERACTION_TIMEOUT;
/**
* Maximum time a foreground service start should keep the buckets elevated if the service
* start is the first usage of the app
*/
- long mInitialForegroundServiceStartTimeoutMillis;
+ long mInitialForegroundServiceStartTimeoutMillis =
+ ConstantsObserver.DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT;
/**
* User usage that would elevate an app's standby bucket will also elevate the standby bucket of
* cross profile connected apps. Explicit standby bucket setting via
* {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated.
*/
- boolean mLinkCrossProfileApps;
+ boolean mLinkCrossProfileApps =
+ ConstantsObserver.DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS;
/**
* Whether we should allow apps into the
* {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
@@ -481,9 +490,8 @@ public class AppStandbyController implements AppStandbyInternal {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
Slog.d(TAG, "Setting app idle enabled state");
// Observe changes to the threshold
- SettingsObserver settingsObserver = new SettingsObserver(mHandler);
- settingsObserver.registerObserver();
- settingsObserver.updateSettings();
+ ConstantsObserver settingsObserver = new ConstantsObserver(mHandler);
+ settingsObserver.start();
mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
@@ -1964,7 +1972,8 @@ public class AppStandbyController implements AppStandbyInternal {
* The minimum amount of time required since the last user interaction before an app can be
* automatically placed in the RESTRICTED bucket.
*/
- long mAutoRestrictedBucketDelayMs = ONE_DAY;
+ long mAutoRestrictedBucketDelayMs =
+ ConstantsObserver.DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS;
/**
* Cached set of apps that are power whitelisted, including those not whitelisted from idle.
*/
@@ -2136,9 +2145,9 @@ public class AppStandbyController implements AppStandbyInternal {
return appWidgetManager.isBoundWidgetPackage(packageName, userId);
}
- String getAppIdleSettings() {
- return Global.getString(mContext.getContentResolver(),
- Global.APP_IDLE_CONSTANTS);
+ @NonNull
+ DeviceConfig.Properties getDeviceConfigProperties(String... keys) {
+ return DeviceConfig.getProperties(DeviceConfig.NAMESPACE_APP_STANDBY, keys);
}
/** Whether the device is in doze or not. */
@@ -2162,6 +2171,12 @@ public class AppStandbyController implements AppStandbyInternal {
return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId);
}
+ void registerDeviceConfigPropertiesChangedListener(
+ @NonNull DeviceConfig.OnPropertiesChangedListener listener) {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_STANDBY,
+ JobSchedulerBackgroundThread.getExecutor(), listener);
+ }
+
void dump(PrintWriter pw) {
pw.println("mPowerWhitelistedApps=[");
synchronized (mPowerWhitelistedApps) {
@@ -2286,11 +2301,11 @@ public class AppStandbyController implements AppStandbyInternal {
};
/**
- * Observe settings changes for {@link Global#APP_IDLE_CONSTANTS}.
+ * Observe changes for {@link DeviceConfig#NAMESPACE_APP_STANDBY} and other standby related
+ * Settings constants.
*/
- private class SettingsObserver extends ContentObserver {
- private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds";
- private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds";
+ private class ConstantsObserver extends ContentObserver implements
+ DeviceConfig.OnPropertiesChangedListener {
private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
"notification_seen_duration";
@@ -2314,33 +2329,69 @@ public class AppStandbyController implements AppStandbyInternal {
"auto_restricted_bucket_delay_ms";
private static final String KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS =
"cross_profile_apps_share_standby_buckets";
- public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
- public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
- public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
- public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT = 4 * ONE_HOUR;
- public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
- public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = ONE_DAY;
+ private static final String KEY_PREFIX_SCREEN_TIME_THRESHOLD = "screen_threshold_";
+ private final String[] KEYS_SCREEN_TIME_THRESHOLDS = {
+ KEY_PREFIX_SCREEN_TIME_THRESHOLD + "active",
+ KEY_PREFIX_SCREEN_TIME_THRESHOLD + "working_set",
+ KEY_PREFIX_SCREEN_TIME_THRESHOLD + "frequent",
+ KEY_PREFIX_SCREEN_TIME_THRESHOLD + "rare",
+ KEY_PREFIX_SCREEN_TIME_THRESHOLD + "restricted"
+ };
+ private static final String KEY_PREFIX_ELAPSED_TIME_THRESHOLD = "elapsed_threshold_";
+ private final String[] KEYS_ELAPSED_TIME_THRESHOLDS = {
+ KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "active",
+ KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "working_set",
+ KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "frequent",
+ KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "rare",
+ KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "restricted"
+ };
+ public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS =
+ COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
+ public static final long DEFAULT_STRONG_USAGE_TIMEOUT =
+ COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR;
+ public static final long DEFAULT_NOTIFICATION_TIMEOUT =
+ COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
+ public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT =
+ COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR;
+ public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT =
+ COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
+ public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT =
+ COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
+ public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT =
+ COMPRESS_TIME ? (ONE_MINUTE / 2) : 10 * ONE_MINUTE;
+ public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT =
+ COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
+ public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT =
+ COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
+ public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT =
+ COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
+ public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT =
+ COMPRESS_TIME ? ONE_MINUTE : 30 * ONE_MINUTE;
+ public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS =
+ COMPRESS_TIME ? ONE_MINUTE : ONE_DAY;
public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
-
- SettingsObserver(Handler handler) {
+ ConstantsObserver(Handler handler) {
super(handler);
}
- void registerObserver() {
+ public void start() {
final ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Global.getUriFor(Global.APP_IDLE_CONSTANTS), false, this);
+ // APP_STANDBY_ENABLED is a SystemApi that some apps may be watching, so best to
+ // leave it in Settings.
cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
+ // Leave ENABLE_RESTRICTED_BUCKET as a user-controlled setting which will stay in
+ // Settings.
+ // TODO: make setting user-specific
cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET),
false, this);
+ // ADAPTIVE_BATTERY_MANAGEMENT_ENABLED is a user setting, so it has to stay in Settings.
cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
false, this);
+ mInjector.registerDeviceConfigPropertiesChangedListener(this);
+ // Load all the constants.
+ onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ updateSettings();
}
@Override
@@ -2349,6 +2400,107 @@ public class AppStandbyController implements AppStandbyInternal {
postOneTimeCheckIdleStates();
}
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ boolean timeThresholdsUpdated = false;
+ synchronized (mAppIdleLock) {
+ for (String name : properties.getKeyset()) {
+ if (name == null) {
+ continue;
+ }
+ switch (name) {
+ case KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS:
+ mInjector.mAutoRestrictedBucketDelayMs = Math.max(
+ COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
+ properties.getLong(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
+ DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
+ break;
+ case KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS:
+ mLinkCrossProfileApps = properties.getBoolean(
+ KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS,
+ DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS);
+ break;
+ case KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION:
+ mInitialForegroundServiceStartTimeoutMillis = properties.getLong(
+ KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
+ DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
+ break;
+ case KEY_NOTIFICATION_SEEN_HOLD_DURATION:
+ mNotificationSeenTimeoutMillis = properties.getLong(
+ KEY_NOTIFICATION_SEEN_HOLD_DURATION,
+ DEFAULT_NOTIFICATION_TIMEOUT);
+ break;
+ case KEY_STRONG_USAGE_HOLD_DURATION:
+ mStrongUsageTimeoutMillis = properties.getLong(
+ KEY_STRONG_USAGE_HOLD_DURATION, DEFAULT_STRONG_USAGE_TIMEOUT);
+ break;
+ case KEY_PREDICTION_TIMEOUT:
+ mPredictionTimeoutMillis = properties.getLong(
+ KEY_PREDICTION_TIMEOUT, DEFAULT_PREDICTION_TIMEOUT);
+ break;
+ case KEY_SYSTEM_INTERACTION_HOLD_DURATION:
+ mSystemInteractionTimeoutMillis = properties.getLong(
+ KEY_SYSTEM_INTERACTION_HOLD_DURATION,
+ DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
+ break;
+ case KEY_SYSTEM_UPDATE_HOLD_DURATION:
+ mSystemUpdateUsageTimeoutMillis = properties.getLong(
+ KEY_SYSTEM_UPDATE_HOLD_DURATION, DEFAULT_SYSTEM_UPDATE_TIMEOUT);
+ break;
+ case KEY_SYNC_ADAPTER_HOLD_DURATION:
+ mSyncAdapterTimeoutMillis = properties.getLong(
+ KEY_SYNC_ADAPTER_HOLD_DURATION, DEFAULT_SYNC_ADAPTER_TIMEOUT);
+ break;
+ case KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION:
+ mExemptedSyncScheduledDozeTimeoutMillis = properties.getLong(
+ KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
+ DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT);
+ break;
+ case KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION:
+ mExemptedSyncScheduledNonDozeTimeoutMillis = properties.getLong(
+ KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION,
+ DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT);
+ break;
+ case KEY_EXEMPTED_SYNC_START_HOLD_DURATION:
+ mExemptedSyncStartTimeoutMillis = properties.getLong(
+ KEY_EXEMPTED_SYNC_START_HOLD_DURATION,
+ DEFAULT_EXEMPTED_SYNC_START_TIMEOUT);
+ break;
+ case KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION:
+ mUnexemptedSyncScheduledTimeoutMillis = properties.getLong(
+ KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
+ DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
+ break;
+ default:
+ if (!timeThresholdsUpdated
+ && (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD)
+ || name.startsWith(KEY_PREFIX_ELAPSED_TIME_THRESHOLD))) {
+ updateTimeThresholds();
+ timeThresholdsUpdated = true;
+ }
+ break;
+ }
+ }
+ }
+ postOneTimeCheckIdleStates();
+ }
+
+ private void updateTimeThresholds() {
+ // Query the values as an atomic set.
+ final DeviceConfig.Properties screenThresholdProperties =
+ mInjector.getDeviceConfigProperties(KEYS_SCREEN_TIME_THRESHOLDS);
+ final DeviceConfig.Properties elapsedThresholdProperties =
+ mInjector.getDeviceConfigProperties(KEYS_ELAPSED_TIME_THRESHOLDS);
+ mAppStandbyScreenThresholds = generateThresholdArray(
+ screenThresholdProperties, KEYS_SCREEN_TIME_THRESHOLDS,
+ DEFAULT_SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS);
+ mAppStandbyElapsedThresholds = generateThresholdArray(
+ elapsedThresholdProperties, KEYS_ELAPSED_TIME_THRESHOLDS,
+ DEFAULT_ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS);
+ mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
+ DEFAULT_CHECK_IDLE_INTERVAL_MS);
+ }
+
void updateSettings() {
if (DEBUG) {
Slog.d(TAG,
@@ -2357,126 +2509,43 @@ public class AppStandbyController implements AppStandbyInternal {
Slog.d(TAG,
"adaptivebat=" + Global.getString(mContext.getContentResolver(),
Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
- Slog.d(TAG, "appidleconstants=" + Global.getString(
- mContext.getContentResolver(),
- Global.APP_IDLE_CONSTANTS));
- }
-
- // Look at global settings for this.
- // TODO: Maybe apply different thresholds for different users.
- try {
- mParser.setString(mInjector.getAppIdleSettings());
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
- // fallthrough, mParser is empty and all defaults will be returned.
}
synchronized (mAppIdleLock) {
-
- String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
- mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
- SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS);
-
- String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
- null);
- mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
- ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS);
- mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
- COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
- mStrongUsageTimeoutMillis = mParser.getDurationMillis(
- KEY_STRONG_USAGE_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STRONG_USAGE_TIMEOUT);
- mNotificationSeenTimeoutMillis = mParser.getDurationMillis(
- KEY_NOTIFICATION_SEEN_HOLD_DURATION,
- COMPRESS_TIME ? 12 * ONE_MINUTE : DEFAULT_NOTIFICATION_TIMEOUT);
- mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis(
- KEY_SYSTEM_UPDATE_HOLD_DURATION,
- COMPRESS_TIME ? 2 * ONE_MINUTE : DEFAULT_SYSTEM_UPDATE_TIMEOUT);
- mPredictionTimeoutMillis = mParser.getDurationMillis(
- KEY_PREDICTION_TIMEOUT,
- COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
- mSyncAdapterTimeoutMillis = mParser.getDurationMillis(
- KEY_SYNC_ADAPTER_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYNC_ADAPTER_TIMEOUT);
-
- mExemptedSyncScheduledNonDozeTimeoutMillis = mParser.getDurationMillis(
- KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION,
- COMPRESS_TIME ? (ONE_MINUTE / 2)
- : DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT);
-
- mExemptedSyncScheduledDozeTimeoutMillis = mParser.getDurationMillis(
- KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE
- : DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT);
-
- mExemptedSyncStartTimeoutMillis = mParser.getDurationMillis(
- KEY_EXEMPTED_SYNC_START_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE
- : DEFAULT_EXEMPTED_SYNC_START_TIMEOUT);
-
- mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis(
- KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
- COMPRESS_TIME
- ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
-
- mSystemInteractionTimeoutMillis = mParser.getDurationMillis(
- KEY_SYSTEM_INTERACTION_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
-
- mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis(
- KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE :
- DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
-
- mInjector.mAutoRestrictedBucketDelayMs = Math.max(
- COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
- mParser.getDurationMillis(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
- COMPRESS_TIME
- ? ONE_MINUTE : DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
-
- mLinkCrossProfileApps = mParser.getBoolean(
- KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS,
- DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS);
-
mAllowRestrictedBucket = mInjector.isRestrictedBucketEnabled();
}
- // Check if app_idle_enabled has changed. Do this after getting the rest of the settings
- // in case we need to change something based on the new values.
setAppIdleEnabled(mInjector.isAppIdleEnabled());
}
- long[] parseLongArray(String values, long[] defaults, long[] minValues) {
- if (values == null) return defaults;
- if (values.isEmpty()) {
+ long[] generateThresholdArray(@NonNull DeviceConfig.Properties properties,
+ @NonNull String[] keys, long[] defaults, long[] minValues) {
+ if (properties.getKeyset().isEmpty()) {
// Reset to defaults
return defaults;
- } else {
- String[] thresholds = values.split("/");
- if (thresholds.length == THRESHOLD_BUCKETS.length) {
- if (minValues.length != THRESHOLD_BUCKETS.length) {
- Slog.wtf(TAG, "minValues array is the wrong size");
- // Use zeroes as the minimums.
- minValues = new long[THRESHOLD_BUCKETS.length];
- }
- long[] array = new long[THRESHOLD_BUCKETS.length];
- for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
- try {
- if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) {
- array[i] = Math.max(minValues[i],
- Duration.parse(thresholds[i]).toMillis());
- } else {
- array[i] = Math.max(minValues[i], Long.parseLong(thresholds[i]));
- }
- } catch (NumberFormatException|DateTimeParseException e) {
- return defaults;
- }
- }
- return array;
- } else {
- return defaults;
- }
}
+ if (keys.length != THRESHOLD_BUCKETS.length) {
+ // This should only happen in development.
+ throw new IllegalStateException(
+ "# keys (" + keys.length + ") != # buckets ("
+ + THRESHOLD_BUCKETS.length + ")");
+ }
+ if (defaults.length != THRESHOLD_BUCKETS.length) {
+ // This should only happen in development.
+ throw new IllegalStateException(
+ "# defaults (" + defaults.length + ") != # buckets ("
+ + THRESHOLD_BUCKETS.length + ")");
+ }
+ if (minValues.length != THRESHOLD_BUCKETS.length) {
+ Slog.wtf(TAG, "minValues array is the wrong size");
+ // Use zeroes as the minimums.
+ minValues = new long[THRESHOLD_BUCKETS.length];
+ }
+ long[] array = new long[THRESHOLD_BUCKETS.length];
+ for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
+ array[i] = Math.max(minValues[i], properties.getLong(keys[i], defaults[i]));
+ }
+ return array;
}
}
}
diff --git a/api/current.txt b/api/current.txt
index 26d77ef4318e..9ff7cc255bf4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40338,6 +40338,7 @@ package android.provider {
field public static final String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
field public static final String AUTHORITY = "media";
field @NonNull public static final android.net.Uri AUTHORITY_URI;
+ field public static final String EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT = "android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT";
field public static final String EXTRA_BRIGHTNESS = "android.provider.extra.BRIGHTNESS";
field public static final String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
field public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
@@ -44150,13 +44151,13 @@ package android.service.notification {
method public boolean canBubble();
method public boolean canShowBadge();
method public android.app.NotificationChannel getChannel();
+ method @Nullable public android.content.pm.ShortcutInfo getConversationShortcutInfo();
method public int getImportance();
method public CharSequence getImportanceExplanation();
method public String getKey();
method public long getLastAudiblyAlertedMillis();
method public String getOverrideGroupKey();
method public int getRank();
- method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
method public int getSuppressedVisualEffects();
@@ -57227,8 +57228,8 @@ package android.view.inputmethod {
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
- method public CharSequence getTextAfterCursor(int, int);
- method public CharSequence getTextBeforeCursor(int, int);
+ method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
+ method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
@@ -57454,8 +57455,8 @@ package android.view.inputmethod {
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
- method public CharSequence getTextAfterCursor(int, int);
- method public CharSequence getTextBeforeCursor(int, int);
+ method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
+ method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
@@ -57489,8 +57490,8 @@ package android.view.inputmethod {
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
- method public CharSequence getTextAfterCursor(int, int);
- method public CharSequence getTextBeforeCursor(int, int);
+ method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
+ method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 872f1f2c0591..8ca290fb1b09 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -185,6 +185,7 @@ package android.provider {
public final class DeviceConfig {
field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
+ field public static final String NAMESPACE_APP_STANDBY = "app_standby";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 6d92db45c874..0b95c6d5bd91 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -682,7 +682,7 @@ package android.app {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
- method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
@@ -2202,6 +2202,7 @@ package android.content.pm {
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
+ method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int);
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
@@ -2280,11 +2281,16 @@ package android.content.pm {
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
+ field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
field public static final int MODULE_APEX_NAME = 1; // 0x1
field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
field public static final int RESTRICTION_NONE = 0; // 0x0
+ field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0; // 0x0
+ field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1; // 0x1
+ field public static final int SYSTEM_APP_STATE_INSTALLED = 2; // 0x2
+ field public static final int SYSTEM_APP_STATE_UNINSTALLED = 3; // 0x3
}
public abstract static class PackageManager.DexModuleRegisterCallback {
@@ -7578,8 +7584,11 @@ package android.net.wifi {
field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
+ field @Deprecated public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004; // 0x3ec
field @Deprecated public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; // 0x3e9
field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
+ field @Deprecated public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003; // 0x3eb
+ field @Deprecated public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002; // 0x3ea
field @Deprecated public boolean allowAutojoin;
field @Deprecated public int carrierId;
field @Deprecated public String creatorName;
@@ -10032,6 +10041,7 @@ package android.service.autofill {
public static final class Dataset.Builder {
ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData);
method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
}
@@ -12036,7 +12046,8 @@ package android.telephony.data {
method public int getMtuV6();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
method public int getProtocolType();
- method public int getSuggestedRetryTime();
+ method public long getRetryIntervalMillis();
+ method @Deprecated public int getSuggestedRetryTime();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1
@@ -12048,6 +12059,7 @@ package android.telephony.data {
field public static final int LINK_STATUS_DORMANT = 1; // 0x1
field public static final int LINK_STATUS_INACTIVE = 0; // 0x0
field public static final int LINK_STATUS_UNKNOWN = -1; // 0xffffffff
+ field public static final int RETRY_INTERVAL_UNDEFINED = -1; // 0xffffffff
}
public static final class DataCallResponse.Builder {
@@ -12066,7 +12078,8 @@ package android.telephony.data {
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryIntervalMillis(long);
+ method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
}
public final class DataProfile implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 6164e776a79f..785463a92c45 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -131,6 +131,7 @@ package android.app {
public class ActivityTaskManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context);
+ method public static int getMaxNumPictureInPictureActions(@NonNull android.content.Context);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void moveTaskToRootTask(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean moveTopActivityToPinnedRootTask(int, @NonNull android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksInWindowingModes(@NonNull int[]);
@@ -269,6 +270,7 @@ package android.app {
method public void disallowAssistantAdjustment(String);
method public android.content.ComponentName getEffectsSuppressor();
method public boolean matchesCallFilter(android.os.Bundle);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel);
}
@@ -286,6 +288,7 @@ package android.app {
public class TaskInfo {
method @NonNull public android.content.res.Configuration getConfiguration();
+ method @Nullable public android.app.PictureInPictureParams getPictureInPictureParams();
method @NonNull public android.window.WindowContainerToken getToken();
}
@@ -1556,6 +1559,19 @@ package android.service.autofill {
method @Nullable public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions();
}
+ public final class Dataset implements android.os.Parcelable {
+ method @Nullable public android.content.IntentSender getAuthentication();
+ method @Nullable public android.content.ClipData getFieldContent();
+ method @Nullable public java.util.ArrayList<android.view.autofill.AutofillId> getFieldIds();
+ method @Nullable public java.util.ArrayList<android.view.autofill.AutofillValue> getFieldValues();
+ method @Nullable public String getId();
+ method public boolean isEmpty();
+ }
+
+ public static final class Dataset.Builder {
+ method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData);
+ }
+
public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception;
}
@@ -2339,6 +2355,15 @@ package android.widget.inline {
package android.window {
+ public final class DisplayAreaAppearedInfo implements android.os.Parcelable {
+ ctor public DisplayAreaAppearedInfo(@NonNull android.window.DisplayAreaInfo, @NonNull android.view.SurfaceControl);
+ method public int describeContents();
+ method @NonNull public android.window.DisplayAreaInfo getDisplayAreaInfo();
+ method @NonNull public android.view.SurfaceControl getLeash();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.DisplayAreaAppearedInfo> CREATOR;
+ }
+
public final class DisplayAreaInfo implements android.os.Parcelable {
ctor public DisplayAreaInfo(@NonNull android.window.WindowContainerToken, int, int);
method public int describeContents();
@@ -2354,7 +2379,7 @@ package android.window {
ctor public DisplayAreaOrganizer();
method public void onDisplayAreaAppeared(@NonNull android.window.DisplayAreaInfo, @NonNull android.view.SurfaceControl);
method public void onDisplayAreaVanished(@NonNull android.window.DisplayAreaInfo);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void registerOrganizer(int);
+ method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.DisplayAreaAppearedInfo> registerOrganizer(int);
field public static final int FEATURE_DEFAULT_TASK_CONTAINER = 1; // 0x1
field public static final int FEATURE_ONE_HANDED = 3; // 0x3
field public static final int FEATURE_ROOT = 0; // 0x0
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3c55bce4fc06..2ac345d2560a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2883,11 +2883,7 @@ public class Activity extends ContextThemeWrapper
* but will always be at least three.
*/
public int getMaxNumPictureInPictureActions() {
- try {
- return ActivityTaskManager.getService().getMaxNumPictureInPictureActions(mToken);
- } catch (RemoteException e) {
- return 0;
- }
+ return ActivityTaskManager.getMaxNumPictureInPictureActions(this);
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4c9d017dfc1a..ec287fe10634 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -503,8 +503,7 @@ public class ActivityManager {
@UnsupportedAppUsage
public static final int PROCESS_STATE_TOP = 2;
- /** @hide Process is bound to a TOP app. This is ranked below SERVICE_LOCATION so that
- * it doesn't get the capability of location access while-in-use. */
+ /** @hide Process is bound to a TOP app. */
public static final int PROCESS_STATE_BOUND_TOP = 3;
/** @hide Process is hosting a foreground service. */
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index c9b009becac4..c7b90897c8e7 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -437,6 +437,12 @@ public class ActivityTaskManager {
return currentUiModeSupportsErrorDialogs(config);
}
+ /** @return max allowed number of actions in picture-in-picture mode. */
+ public static int getMaxNumPictureInPictureActions(@NonNull Context context) {
+ return context.getResources().getInteger(
+ com.android.internal.R.integer.config_pictureInPictureMaxNumberOfActions);
+ }
+
/**
* Information you can retrieve about a root task in the system.
* @hide
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 8a03fcc33d51..bd5913efdecb 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -303,7 +303,6 @@ interface IActivityTaskManager {
boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
void requestPictureInPictureMode(in IBinder token);
- int getMaxNumPictureInPictureActions(in IBinder token);
IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
/**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index c052186bb974..d798f22a6af9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -177,9 +177,9 @@ interface INotificationManager
boolean isNotificationListenerAccessGranted(in ComponentName listener);
boolean isNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId);
boolean isNotificationAssistantAccessGranted(in ComponentName assistant);
- void setNotificationListenerAccessGranted(in ComponentName listener, boolean enabled);
+ void setNotificationListenerAccessGranted(in ComponentName listener, boolean enabled, boolean userSet);
void setNotificationAssistantAccessGranted(in ComponentName assistant, boolean enabled);
- void setNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId, boolean enabled);
+ void setNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId, boolean enabled, boolean userSet);
void setNotificationAssistantAccessGrantedForUser(in ComponentName assistant, int userId, boolean enabled);
List<String> getEnabledNotificationListenerPackages();
List<ComponentName> getEnabledNotificationListeners(int userId);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 27cd78acb35f..12460ba2bd4b 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1565,6 +1565,12 @@ public class NotificationManager {
}
}
+ /** @hide */
+ public void setNotificationListenerAccessGranted(
+ @NonNull ComponentName listener, boolean granted) {
+ setNotificationListenerAccessGranted(listener, granted, true);
+ }
+
/**
* Grants/revokes Notification Listener access to the given component for current user.
* To grant access for a particular user, obtain this service by using the {@link Context}
@@ -1572,15 +1578,17 @@ public class NotificationManager {
*
* @param listener Name of component to grant/revoke access
* @param granted Grant/revoke access
+ * @param userSet Whether the action was triggered explicitly by user
* @hide
*/
@SystemApi
+ @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS)
public void setNotificationListenerAccessGranted(
- @NonNull ComponentName listener, boolean granted) {
+ @NonNull ComponentName listener, boolean granted, boolean userSet) {
INotificationManager service = getService();
try {
- service.setNotificationListenerAccessGranted(listener, granted);
+ service.setNotificationListenerAccessGranted(listener, granted, userSet);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1591,7 +1599,7 @@ public class NotificationManager {
boolean granted) {
INotificationManager service = getService();
try {
- service.setNotificationListenerAccessGrantedForUser(listener, userId, granted);
+ service.setNotificationListenerAccessGrantedForUser(listener, userId, granted, true);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4b3bebe36c29..73777909d417 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -136,6 +136,8 @@ import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
+import android.net.vcn.IVcnManagementService;
+import android.net.vcn.VcnManager;
import android.net.wifi.WifiFrameworkInitializer;
import android.net.wifi.nl80211.WifiNl80211Manager;
import android.nfc.NfcManager;
@@ -385,6 +387,14 @@ public final class SystemServiceRegistry {
ctx, () -> ServiceManager.getService(Context.TETHERING_SERVICE));
}});
+ registerService(Context.VCN_MANAGEMENT_SERVICE, VcnManager.class,
+ new CachedServiceFetcher<VcnManager>() {
+ @Override
+ public VcnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.VCN_MANAGEMENT_SERVICE);
+ IVcnManagementService service = IVcnManagementService.Stub.asInterface(b);
+ return new VcnManager(ctx, service);
+ }});
registerService(Context.IPSEC_SERVICE, IpSecManager.class,
new CachedServiceFetcher<IpSecManager>() {
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 8a3ae04a3589..45e9c49c5322 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -241,6 +241,13 @@ public class TaskInfo {
}
/** @hide */
+ @Nullable
+ @TestApi
+ public PictureInPictureParams getPictureInPictureParams() {
+ return pictureInPictureParams;
+ }
+
+ /** @hide */
public void addLaunchCookie(IBinder cookie) {
if (cookie == null || launchCookies.contains(cookie)) return;
launchCookies.add(cookie);
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index d9ea7d237e51..c0c1aa1634ff 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothUuid;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
import android.os.ParcelUuid;
import android.util.ArrayMap;
import android.util.Log;
@@ -196,7 +195,7 @@ public final class ScanRecord {
* @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage
public static ScanRecord parseFromBytes(byte[] scanRecord) {
if (scanRecord == null) {
return null;
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 223005bbf30c..8170bf92ae1e 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -47,6 +47,7 @@ public final class AssociationRequest implements Parcelable {
private final boolean mSingleDevice;
private final List<DeviceFilter<?>> mDeviceFilters;
+ private String mCallingPackage;
private AssociationRequest(
boolean singleDevice, @Nullable List<DeviceFilter<?>> deviceFilters) {
@@ -58,6 +59,7 @@ public final class AssociationRequest implements Parcelable {
this(
in.readByte() != 0,
in.readParcelableList(new ArrayList<>(), AssociationRequest.class.getClassLoader()));
+ setCallingPackage(in.readString());
}
/** @hide */
@@ -73,32 +75,45 @@ public final class AssociationRequest implements Parcelable {
return mDeviceFilters;
}
+ /** @hide */
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /** @hide */
+ public void setCallingPackage(String pkg) {
+ mCallingPackage = pkg;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AssociationRequest that = (AssociationRequest) o;
- return mSingleDevice == that.mSingleDevice &&
- Objects.equals(mDeviceFilters, that.mDeviceFilters);
+ return mSingleDevice == that.mSingleDevice
+ && Objects.equals(mDeviceFilters, that.mDeviceFilters)
+ && Objects.equals(mCallingPackage, that.mCallingPackage);
}
@Override
public int hashCode() {
- return Objects.hash(mSingleDevice, mDeviceFilters);
+ return Objects.hash(mSingleDevice, mDeviceFilters, mCallingPackage);
}
@Override
public String toString() {
- return "AssociationRequest{" +
- "mSingleDevice=" + mSingleDevice +
- ", mDeviceFilters=" + mDeviceFilters +
- '}';
+ return "AssociationRequest{"
+ + "mSingleDevice=" + mSingleDevice
+ + ", mDeviceFilters=" + mDeviceFilters
+ + ", mCallingPackage=" + mCallingPackage
+ + '}';
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (mSingleDevice ? 1 : 0));
dest.writeParcelableList(mDeviceFilters, flags);
+ dest.writeString(mCallingPackage);
}
@Override
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 78eb8591a3e9..c4d98671a598 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -651,45 +651,57 @@ public class ClipData implements Parcelable {
StringBuilder b = new StringBuilder(128);
b.append("ClipData.Item { ");
- toShortString(b);
+ toShortString(b, true);
b.append(" }");
return b.toString();
}
- /** @hide */
- public void toShortString(StringBuilder b) {
+ /**
+ * Appends this item to the given builder.
+ * @param redactContent If true, redacts common forms of PII; otherwise appends full
+ * details.
+ * @hide
+ */
+ public void toShortString(StringBuilder b, boolean redactContent) {
+ boolean first = true;
if (mHtmlText != null) {
- b.append("H:");
- b.append(mHtmlText);
- } else if (mText != null) {
- b.append("T:");
- b.append(mText);
- } else if (mUri != null) {
- b.append("U:");
- b.append(mUri);
- } else if (mIntent != null) {
- b.append("I:");
- mIntent.toShortString(b, true, true, true, true);
- } else {
- b.append("NULL");
+ first = false;
+ if (redactContent) {
+ b.append("H(").append(mHtmlText.length()).append(')');
+ } else {
+ b.append("H:").append(mHtmlText);
+ }
}
- }
-
- /** @hide */
- public void toShortSummaryString(StringBuilder b) {
- if (mHtmlText != null) {
- b.append("HTML");
- } else if (mText != null) {
- b.append("TEXT");
- } else if (mUri != null) {
- b.append("U:");
- b.append(mUri);
- } else if (mIntent != null) {
+ if (mText != null) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ if (redactContent) {
+ b.append("T(").append(mText.length()).append(')');
+ } else {
+ b.append("T:").append(mText);
+ }
+ }
+ if (mUri != null) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ if (redactContent) {
+ b.append("U(").append(mUri.getScheme()).append(')');
+ } else {
+ b.append("U:").append(mUri);
+ }
+ }
+ if (mIntent != null) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
b.append("I:");
- mIntent.toShortString(b, true, true, true, true);
- } else {
- b.append("NULL");
+ mIntent.toShortString(b, redactContent, true, true, true);
}
}
@@ -1040,17 +1052,21 @@ public class ClipData implements Parcelable {
StringBuilder b = new StringBuilder(128);
b.append("ClipData { ");
- toShortString(b);
+ toShortString(b, true);
b.append(" }");
return b.toString();
}
- /** @hide */
- public void toShortString(StringBuilder b) {
+ /**
+ * Appends this clip to the given builder.
+ * @param redactContent If true, redacts common forms of PII; otherwise appends full details.
+ * @hide
+ */
+ public void toShortString(StringBuilder b, boolean redactContent) {
boolean first;
if (mClipDescription != null) {
- first = !mClipDescription.toShortString(b);
+ first = !mClipDescription.toShortString(b, redactContent);
} else {
first = true;
}
@@ -1064,26 +1080,21 @@ public class ClipData implements Parcelable {
b.append('x');
b.append(mIcon.getHeight());
}
- for (int i=0; i<mItems.size(); i++) {
+ if (mItems.size() != 1) {
if (!first) {
b.append(' ');
}
first = false;
- b.append('{');
- mItems.get(i).toShortString(b);
- b.append('}');
+ b.append(mItems.size()).append(" items:");
}
- }
-
- /** @hide */
- public void toShortStringShortItems(StringBuilder b, boolean first) {
- if (mItems.size() > 0) {
+ for (int i = 0; i < mItems.size(); i++) {
if (!first) {
b.append(' ');
}
- for (int i=0; i<mItems.size(); i++) {
- b.append("{...}");
- }
+ first = false;
+ b.append('{');
+ mItems.get(i).toShortString(b, redactContent);
+ b.append('}');
}
}
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index f9e6308515cf..73becb1c0f1c 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -330,30 +330,45 @@ public class ClipDescription implements Parcelable {
StringBuilder b = new StringBuilder(128);
b.append("ClipDescription { ");
- toShortString(b);
+ toShortString(b, true);
b.append(" }");
return b.toString();
}
- /** @hide */
- public boolean toShortString(StringBuilder b) {
+ /**
+ * Appends this description to the given builder.
+ * @param redactContent If true, redacts common forms of PII; otherwise appends full details.
+ * @hide
+ */
+ public boolean toShortString(StringBuilder b, boolean redactContent) {
boolean first = !toShortStringTypesOnly(b);
if (mLabel != null) {
if (!first) {
b.append(' ');
}
first = false;
- b.append('"');
- b.append(mLabel);
- b.append('"');
+ if (redactContent) {
+ b.append("hasLabel(").append(mLabel.length()).append(')');
+ } else {
+ b.append('"').append(mLabel).append('"');
+ }
}
if (mExtras != null) {
if (!first) {
b.append(' ');
}
first = false;
- b.append(mExtras.toString());
+ if (redactContent) {
+ if (mExtras.isParcelled()) {
+ // We don't want this toString function to trigger un-parcelling.
+ b.append("hasExtras");
+ } else {
+ b.append("hasExtras(").append(mExtras.size()).append(')');
+ }
+ } else {
+ b.append(mExtras.toString());
+ }
}
if (mTimeStamp > 0) {
if (!first) {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 03adbc782eef..49248b51a5c7 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -624,6 +624,20 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
+ public void uncanonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ RemoteCallback callback) {
+ final Bundle result = new Bundle();
+ try {
+ result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+ uncanonicalize(callingPkg, attributionTag, uri));
+ } catch (Exception e) {
+ result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
+ new ParcelableException(e));
+ }
+ callback.sendResult(result);
+ }
+
+ @Override
public boolean refresh(String callingPkg, String attributionTag, Uri uri, Bundle extras,
ICancellationSignal cancellationSignal) throws RemoteException {
uri = validateIncomingUri(uri);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 7bc59013bcfe..7d121d56c86d 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -382,6 +382,16 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return true;
}
+ case UNCANONICALIZE_ASYNC_TRANSACTION: {
+ data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
+ String featureId = data.readString();
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
+ uncanonicalizeAsync(callingPkg, featureId, uri, callback);
+ return true;
+ }
+
case REFRESH_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
@@ -875,6 +885,25 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
+ /* oneway */ public void uncanonicalizeAsync(String callingPkg, @Nullable String featureId,
+ Uri uri, RemoteCallback callback) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ try {
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ data.writeString(callingPkg);
+ data.writeString(featureId);
+ uri.writeToParcel(data, 0);
+ callback.writeToParcel(data, 0);
+
+ mRemote.transact(IContentProvider.UNCANONICALIZE_ASYNC_TRANSACTION, data, null,
+ Binder.FLAG_ONEWAY);
+ } finally {
+ data.recycle();
+ }
+ }
+
+ @Override
public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle extras,
ICancellationSignal signal) throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 50092d17c692..422d3f7c6784 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1338,7 +1338,14 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
- return provider.uncanonicalize(mPackageName, mAttributionTag, url);
+ final UriResultListener resultListener = new UriResultListener();
+ provider.uncanonicalizeAsync(mPackageName, mAttributionTag, url,
+ new RemoteCallback(resultListener));
+ resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+ if (resultListener.exception != null) {
+ throw resultListener.exception;
+ }
+ return resultListener.result;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1d7a54c3021c..46d4f222a6b4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3416,6 +3416,7 @@ public abstract class Context {
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
+ VCN_MANAGEMENT_SERVICE,
//@hide: IP_MEMORY_STORE_SERVICE,
IPSEC_SERVICE,
VPN_MANAGEMENT_SERVICE,
@@ -3995,6 +3996,16 @@ public abstract class Context {
public static final String CONNECTIVITY_SERVICE = "connectivity";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager}
+ * for managing Virtual Carrier Networks
+ *
+ * @see #getSystemService(String)
+ * @see android.net.vcn.VcnManager
+ * @hide
+ */
+ public static final String VCN_MANAGEMENT_SERVICE = "vcn_management";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.net.INetd} for communicating with the network stack
* @hide
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index becba67a0198..9210b132c75a 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -137,6 +137,14 @@ public interface IContentProvider extends IInterface {
public Uri uncanonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
throws RemoteException;
+ /**
+ * A oneway version of uncanonicalize. The functionality is exactly the same, except that the
+ * call returns immediately, and the resulting type is returned when available via
+ * a binder callback.
+ */
+ void uncanonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ RemoteCallback callback) throws RemoteException;
+
public boolean refresh(String callingPkg, @Nullable String attributionTag, Uri url,
@Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException;
@@ -172,4 +180,5 @@ public interface IContentProvider extends IInterface {
static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27;
int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28;
int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29;
+ int UNCANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 30;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 06ac5dbed702..782f0cd04dfc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -10485,17 +10485,6 @@ public class Intent implements Parcelable, Cloneable {
}
/** @hide */
- public String toInsecureStringWithClip() {
- StringBuilder b = new StringBuilder(128);
-
- b.append("Intent { ");
- toShortString(b, false, true, true, true);
- b.append(" }");
-
- return b.toString();
- }
-
- /** @hide */
public String toShortString(boolean secure, boolean comp, boolean extras, boolean clip) {
StringBuilder b = new StringBuilder(128);
toShortString(b, secure, comp, extras, clip);
@@ -10581,16 +10570,7 @@ public class Intent implements Parcelable, Cloneable {
b.append(' ');
}
b.append("clip={");
- if (clip) {
- mClipData.toShortString(b);
- } else {
- if (mClipData.getDescription() != null) {
- first = !mClipData.getDescription().toShortStringTypesOnly(b);
- } else {
- first = true;
- }
- mClipData.toShortStringShortItems(b, first);
- }
+ mClipData.toShortString(b, !clip || secure);
first = false;
b.append('}');
}
@@ -10668,11 +10648,7 @@ public class Intent implements Parcelable, Cloneable {
}
if (mClipData != null) {
StringBuilder b = new StringBuilder();
- if (clip) {
- mClipData.toShortString(b);
- } else {
- mClipData.toShortStringShortItems(b, false);
- }
+ mClipData.toShortString(b, !clip || secure);
proto.write(IntentProto.CLIP_DATA, b.toString());
}
if (extras && mExtras != null) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index dfa9093301c3..c0f581717641 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -567,9 +567,11 @@ public abstract class PackageManager {
public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
/**
- * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
+ * {@link PackageInfo} flag: include system apps that are in the uninstalled state and have
+ * been set to be hidden until installed via {@link #setSystemAppState}.
* @hide
*/
+ @SystemApi
public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000;
/**
@@ -3768,27 +3770,34 @@ public abstract class PackageManager {
public @interface SystemAppState {}
/**
- * Constant for noting system app state as hidden before installation
+ * Constant for use with {@link #setSystemAppState} to mark a system app as hidden until
+ * installation.
* @hide
*/
+ @SystemApi
public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0;
/**
- * Constant for noting system app state as visible before installation
+ * Constant for use with {@link #setSystemAppState} to mark a system app as not hidden until
+ * installation.
* @hide
*/
+ @SystemApi
public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1;
/**
- * Constant for noting system app state as installed
+ * Constant for use with {@link #setSystemAppState} to change a system app's state to installed.
* @hide
*/
+ @SystemApi
public static final int SYSTEM_APP_STATE_INSTALLED = 2;
/**
- * Constant for noting system app state as not installed
+ * Constant for use with {@link #setSystemAppState} to change a system app's state to
+ * uninstalled.
* @hide
*/
+ @SystemApi
public static final int SYSTEM_APP_STATE_UNINSTALLED = 3;
/**
@@ -7092,11 +7101,18 @@ public abstract class PackageManager {
@NonNull UserHandle userHandle);
/**
- * Sets system app state
+ * Sets the state of a system app.
+ *
+ * This method can be used to change a system app's hidden-until-installed state (via
+ * {@link #SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN} and
+ * {@link #SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE} or its installation state (via
+ * {@link #SYSTEM_APP_STATE_INSTALLED} and {@link #SYSTEM_APP_STATE_UNINSTALLED}.
+ *
* @param packageName Package name of the app.
* @param state State of the app.
* @hide
*/
+ @SystemApi
public void setSystemAppState(@NonNull String packageName, @SystemAppState int state) {
throw new RuntimeException("Not implemented. Must override in a subclass");
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 7ed803f78260..6a6be75c4606 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -1212,8 +1212,13 @@ public final class ShortcutInfo implements Parcelable {
}
/**
- * Sets categories for a shortcut. Launcher apps may use this information to
- * categorize shortcuts.
+ * Sets categories for a shortcut.
+ * <ul>
+ * <li>Launcher apps may use this information to categorize shortcuts
+ * <li> Used by the system to associate a published Sharing Shortcut with supported
+ * mimeTypes. Required for published Sharing Shortcuts with a matching category
+ * declared in share targets, defined in the app's manifest linked shortcuts xml file.
+ * </ul>
*
* @see #SHORTCUT_CATEGORY_CONVERSATION
* @see ShortcutInfo#getCategories()
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 63397c05edc2..d2c74e963689 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -419,6 +419,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * Use the provided handler thread for events.
+ * @param handler
+ */
+ private void useHandler(Handler handler) {
+ if (handler != null) {
+ mHandler = new MyHandler(handler.getLooper());
+ } else if (mHandler.getLooper() != mContext.getMainLooper()) {
+ mHandler = new MyHandler(mContext.getMainLooper());
+ }
+ }
+
+ /**
* Request authentication of a crypto object. This call warms up the fingerprint hardware
* and starts scanning for a fingerprint. It terminates when
* {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
@@ -449,45 +461,15 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
- * Use the provided handler thread for events.
- * @param handler
- */
- private void useHandler(Handler handler) {
- if (handler != null) {
- mHandler = new MyHandler(handler.getLooper());
- } else if (mHandler.getLooper() != mContext.getMainLooper()){
- mHandler = new MyHandler(mContext.getMainLooper());
- }
- }
-
- /**
- * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal,
- * AuthenticationCallback, Handler, int, Surface)} with {@code surface} set to null.
- *
- * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal,
- * AuthenticationCallback, Handler, int, Surface)
- *
- * @hide
- */
- @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
- public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, Handler handler, int userId) {
- authenticate(crypto, cancel, callback, handler, userId, null /* surface */);
- }
-
- /**
* Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
* CancellationSignal, int, AuthenticationCallback, Handler)}. This version does not
* display the BiometricPrompt.
* @param userId the user ID that the fingerprint hardware will authenticate for.
- * @param surface for optical fingerprint sensors that require active illumination by the OLED
- * display. Should be null for devices that don't require illumination.
* @hide
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, Handler handler, int userId,
- @Nullable Surface surface) {
+ @NonNull AuthenticationCallback callback, Handler handler, int userId) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
@@ -508,7 +490,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
mCryptoObject = crypto;
final long operationId = crypto != null ? crypto.getOpId() : 0;
mService.authenticate(mToken, operationId, userId, mServiceReceiver,
- mContext.getOpPackageName(), surface);
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception while authenticating: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or try
@@ -527,7 +509,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void detectFingerprint(@NonNull CancellationSignal cancel,
- @NonNull FingerprintDetectionCallback callback, int userId, @Nullable Surface surface) {
+ @NonNull FingerprintDetectionCallback callback, int userId) {
if (mService == null) {
return;
}
@@ -543,28 +525,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
try {
mService.detectFingerprint(mToken, userId, mServiceReceiver,
- mContext.getOpPackageName(), surface);
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception when requesting finger detect", e);
}
}
/**
- * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int,
- * EnrollmentCallback, Surface)} with {@code surface} set to null.
- *
- * @see FingerprintManager#enroll(byte[], CancellationSignal, int, EnrollmentCallback,
- * Surface)
- *
- * @hide
- */
- @RequiresPermission(MANAGE_FINGERPRINT)
- public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
- EnrollmentCallback callback) {
- enroll(hardwareAuthToken, cancel, userId, callback, null /* surface */);
- }
-
- /**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
@@ -581,7 +548,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
- EnrollmentCallback callback, @Nullable Surface surface) {
+ EnrollmentCallback callback) {
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentUserId();
}
@@ -602,7 +569,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
try {
mEnrollmentCallback = callback;
mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
- mContext.getOpPackageName(), surface);
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or try
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 1f896cdf4298..9a9e47868b85 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -28,12 +28,41 @@ import android.os.Parcel;
* @hide
*/
public class FingerprintSensorPropertiesInternal extends SensorPropertiesInternal {
+ /**
+ * See {@link FingerprintSensorProperties.SensorType}.
+ */
public final @FingerprintSensorProperties.SensorType int sensorType;
- // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
- // cannot be checked
+
+ /**
+ * IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
+ * cannot be checked
+ */
public final boolean resetLockoutRequiresHardwareAuthToken;
/**
+ * The location of the center of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
+ * distance in pixels, measured from the left edge of the screen.
+ * TODO: Value should be provided from the HAL
+ */
+ public final int sensorLocationX = 540;
+
+ /**
+ * The location of the center of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
+ * distance in pixels, measured from the top edge of the screen.
+ * TODO: Value should be provided from the HAL
+ */
+ public final int sensorLocationY = 1636;
+
+ /**
+ * The radius of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius
+ * of the sensor, in pixels.
+ */
+ public final int sensorRadius = 130;
+
+ /**
* Initializes SensorProperties with specified values
*/
public FingerprintSensorPropertiesInternal(int sensorId,
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 95668372c5a1..547de9d3c86a 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -23,7 +23,6 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.view.Surface;
import java.util.List;
/**
@@ -42,12 +41,12 @@ interface IFingerprintService {
// USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes
// through FingerprintManager now.
void authenticate(IBinder token, long operationId, int userId,
- IFingerprintServiceReceiver receiver, String opPackageName, in Surface surface);
+ IFingerprintServiceReceiver receiver, String opPackageName);
// Uses the fingerprint hardware to detect for the presence of a finger, without giving details
// about accept/reject/lockout.
void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
- String opPackageName, in Surface surface);
+ String opPackageName);
// This method prepares the service to start authenticating, but doesn't start authentication.
// This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
@@ -56,7 +55,7 @@ interface IFingerprintService {
// startPreparedClient().
void prepareForAuthentication(IBinder token, long operationId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
- int callingUid, int callingPid, int callingUserId, in Surface surface);
+ int callingUid, int callingPid, int callingUserId);
// Starts authentication with the previously prepared client.
void startPreparedClient(int cookie);
@@ -74,7 +73,7 @@ interface IFingerprintService {
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
- String opPackageName, in Surface surface);
+ String opPackageName);
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index f75b88f8e24d..7fb73ceab4f4 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -231,7 +231,7 @@ public final class InputManager {
* Waits for the event to be delivered to the application and handled.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171972397)
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH =
InputEventInjectionSync.WAIT_FOR_FINISHED;
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 0512305e71a2..bb2357e24617 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -280,7 +280,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
CountDownLatch latch = new CountDownLatch(1);
- mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP,
+ mCaller.getHandler().sendMessageAtFrontOfQueue(mCaller.obtainMessageOOOO(DO_DUMP,
fd, fout, args, latch));
try {
if (!latch.await(5, TimeUnit.SECONDS)) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 070bec11b93a..d290465816b6 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -102,6 +102,7 @@ import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets.Side;
+import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.CompletionInfo;
@@ -135,7 +136,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -883,10 +884,19 @@ public class InputMethodService extends AbstractInputMethodService {
/** Set region of the keyboard to be avoided from back gesture */
private void setImeExclusionRect(int visibleTopInsets) {
- View inputFrameRootView = mInputFrame.getRootView();
- Rect r = new Rect(0, visibleTopInsets, inputFrameRootView.getWidth(),
- inputFrameRootView.getHeight());
- inputFrameRootView.setSystemGestureExclusionRects(Collections.singletonList(r));
+ View rootView = mInputFrame.getRootView();
+ android.graphics.Insets systemGesture =
+ rootView.getRootWindowInsets().getInsets(Type.systemGestures());
+ ArrayList<Rect> exclusionRects = new ArrayList<>();
+ exclusionRects.add(new Rect(0,
+ visibleTopInsets,
+ systemGesture.left,
+ rootView.getHeight()));
+ exclusionRects.add(new Rect(rootView.getWidth() - systemGesture.right,
+ visibleTopInsets,
+ rootView.getWidth(),
+ rootView.getHeight()));
+ rootView.setSystemGestureExclusionRects(exclusionRects);
}
/**
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 327e42bdd2f7..44ebff99f3e9 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -353,10 +353,11 @@ public abstract class NetworkAgent {
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
// The subtype can be changed with (TODO) setLegacySubtype, but it starts
- // with the type and an empty description.
+ // with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description.
final NetworkInfo ni = new NetworkInfo(config.legacyType, 0, config.legacyTypeName, "");
ni.setIsAvailable(true);
- ni.setExtraInfo(config.getLegacyExtraInfo());
+ ni.setDetailedState(NetworkInfo.DetailedState.CONNECTING, null /* reason */,
+ config.getLegacyExtraInfo());
return ni;
}
diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING
index abac81117deb..8c13ef98bedb 100644
--- a/core/java/android/net/TEST_MAPPING
+++ b/core/java/android/net/TEST_MAPPING
@@ -17,4 +17,4 @@
"path": "frameworks/opt/net/wifi"
}
]
-} \ No newline at end of file
+}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index 0b279b882367..af06906ca2e9 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2020 The Android Open Source Project
+/*
+ * Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net;
-@JavaOnlyStableParcelable parcelable TetheredClient; \ No newline at end of file
+package android.net.vcn;
+
+/**
+ * @hide
+ */
+interface IVcnManagementService {
+}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
new file mode 100644
index 000000000000..d563b0350187
--- /dev/null
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks
+ *
+ * @hide
+ */
+@SystemService(Context.VCN_MANAGEMENT_SERVICE)
+public final class VcnManager {
+ @NonNull private static final String TAG = VcnManager.class.getSimpleName();
+
+ @NonNull private final Context mContext;
+ @NonNull private final IVcnManagementService mService;
+
+ /**
+ * Construct an instance of VcnManager within an application context.
+ *
+ * @param ctx the application context for this manager
+ * @param service the VcnManagementService binder backing this manager
+ *
+ * @hide
+ */
+ public VcnManager(@NonNull Context ctx, @NonNull IVcnManagementService service) {
+ mContext = requireNonNull(ctx, "missing context");
+ mService = requireNonNull(service, "missing service");
+ }
+}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index e738cb276d00..e1d900528f07 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -117,11 +117,6 @@ interface INetworkManagementService
void removeRoute(int netId, in RouteInfo route);
/**
- * Set the specified MTU size
- */
- void setMtu(String iface, int mtu);
-
- /**
* Shuts down the service
*/
void shutdown();
@@ -350,28 +345,8 @@ interface INetworkManagementService
*/
boolean isNetworkActive();
- /**
- * Add an interface to a network.
- */
- void addInterfaceToNetwork(String iface, int netId);
-
- /**
- * Remove an Interface from a network.
- */
- void removeInterfaceFromNetwork(String iface, int netId);
-
void addLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid);
- void setDefaultNetId(int netId);
- void clearDefaultNetId();
-
- /**
- * Set permission for a network.
- * @param permission PERMISSION_NONE to clear permissions.
- * PERMISSION_NETWORK or PERMISSION_SYSTEM to set permission.
- */
- void setNetworkPermission(int netId, int permission);
-
/**
* Allow UID to call protect().
*/
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index aa2ad115a48f..8048b9df6097 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -725,7 +725,7 @@ public class Process {
* Returns the identifier of this process' parent.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171962076)
public static final int myPpid() {
return Os.getppid();
}
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 1cd9c1a457a9..136e3de731a9 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -158,7 +158,7 @@ public final class SharedMemory implements Parcelable, Closeable {
*
* @hide Exposed for native ASharedMemory_dupFromJava()
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171971817)
public int getFd() {
return mFileDescriptor.getInt$();
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index af86a24caff4..2edd3227cf36 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -67,7 +67,7 @@ import java.util.Set;
/**
* Manages users and user details on a multi-user system. There are two major categories of
- * users: fully customizable users with their own login, and managed profiles that share a workspace
+ * users: fully customizable users with their own login, and profiles that share a workspace
* with a related user.
* <p>
* Users are different from accounts, which are managed by
@@ -2298,7 +2298,7 @@ public class UserManager {
* private app data storage is available.
* <p>Requires {@code android.permission.MANAGE_USERS} or
* {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
- * must be the calling user or a managed profile associated with it.
+ * must be the calling user or a profile associated with it.
*
* @param user to retrieve the unlocked state for.
* @see Intent#ACTION_USER_UNLOCKED
@@ -2496,7 +2496,7 @@ public class UserManager {
*
* <p>Requires {@code android.permission.MANAGE_USERS} or
* {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
- * must be the calling user or a managed profile associated with it.
+ * must be the calling user or a profile associated with it.
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_USERS,
@@ -2620,7 +2620,7 @@ public class UserManager {
*
* <p>Requires {@code android.permission.MANAGE_USERS} or
* {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
- * must be the calling user or a managed profile associated with it.
+ * must be the calling user or a profile associated with it.
*
* @hide
*/
@@ -3429,6 +3429,7 @@ public class UserManager {
* Returns list of the profiles of userId including userId itself.
* Note that this returns both enabled and not enabled profiles. See
* {@link #getEnabledProfiles(int)} if you need only the enabled ones.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
*
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
* {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user.
@@ -3481,6 +3482,7 @@ public class UserManager {
/**
* Returns list of the profiles of userId including userId itself.
* Note that this returns only enabled.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
*
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
* {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user.
@@ -3502,6 +3504,7 @@ public class UserManager {
/**
* Returns a list of UserHandles for profiles associated with the user that the calling process
* is running on, including the user itself.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
*
* @return A non-empty list of UserHandles associated with the calling user.
*/
@@ -3517,6 +3520,7 @@ public class UserManager {
/**
* Returns a list of ids for enabled profiles associated with the context user including the
* user itself.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
*
* @return A non-empty list of UserHandles associated with the calling user.
* @hide
@@ -3532,6 +3536,7 @@ public class UserManager {
/**
* Returns a list of ids for all profiles associated with the context user including the user
* itself.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
*
* @return A non-empty list of UserHandles associated with the calling user.
* @hide
@@ -3547,6 +3552,7 @@ public class UserManager {
/**
* Returns a list of ids for profiles associated with the context user including the user
* itself.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
*
* @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles
* @return A non-empty list of UserHandles associated with the calling user.
@@ -3566,6 +3572,7 @@ public class UserManager {
/**
* Returns a list of ids for profiles associated with the specified user including the user
* itself.
+ * <p>Note that this includes all profile types (not including Restricted profiles).
*
* @param userId id of the user to return profiles for
* @param enabledOnly whether return only {@link UserInfo#isEnabled() enabled} profiles
@@ -4355,8 +4362,9 @@ public class UserManager {
}
/**
- * Returns creation time of the user or of a managed profile associated with the calling user.
- * @param userHandle user handle of the user or a managed profile associated with the
+ * Returns creation time of the given user. The given user must be the calling user or
+ * a profile associated with it.
+ * @param userHandle user handle of the calling user or a profile associated with the
* calling user.
* @return creation time in milliseconds since Epoch time.
*/
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 0315b561c120..0a4e867458e0 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -101,6 +101,14 @@ public final class DeviceConfig {
public static final String NAMESPACE_APP_COMPAT = "app_compat";
/**
+ * Namespace for app standby configurations.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final String NAMESPACE_APP_STANDBY = "app_standby";
+
+ /**
* Namespace for AttentionManagerService related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 21c4a11e57ac..780c4fa66d26 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6334,9 +6334,10 @@ public final class Settings {
* Comma-separated list of location providers that are enabled. Do not rely on this value
* being present or correct, or on ContentObserver notifications on the corresponding Uri.
*
- * @deprecated The preferred methods for checking provider status and listening for changes
- * are via {@link LocationManager#isProviderEnabled(String)} and
- * {@link LocationManager#PROVIDERS_CHANGED_ACTION}.
+ * @deprecated This setting no longer exists from Android S onwards as it no longer is
+ * capable of realistically reflecting location settings. Use {@link
+ * LocationManager#isProviderEnabled(String)} or {@link LocationManager#isLocationEnabled()}
+ * instead.
*/
@Deprecated
public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
@@ -9054,12 +9055,6 @@ public final class Settings {
public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked";
/**
- * Controls if window magnification is enabled.
- * @hide
- */
- public static final String WINDOW_MAGNIFICATION = "window_magnification";
-
- /**
* Controls magnification mode when magnification is enabled via a system-wide triple tap
* gesture or the accessibility shortcut.
*
@@ -11838,37 +11833,6 @@ public final class Settings {
public static final String APP_TIME_LIMIT_USAGE_SOURCE = "app_time_limit_usage_source";
/**
- * App standby (app idle) specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- * <p>
- * "idle_duration=5000,prediction_timeout=4500,screen_thresholds=0/0/60000/120000"
- * <p>
- * All durations are in millis.
- * Array values are separated by forward slashes
- * The following keys are supported:
- *
- * <pre>
- * screen_thresholds (long[4])
- * elapsed_thresholds (long[4])
- * strong_usage_duration (long)
- * notification_seen_duration (long)
- * system_update_usage_duration (long)
- * prediction_timeout (long)
- * sync_adapter_duration (long)
- * exempted_sync_duration (long)
- * system_interaction_duration (long)
- * initial_foreground_service_start_duration (long)
- * cross_profile_apps_share_standby_buckets (boolean)
- * </pre>
- *
- * <p>
- * Type: string
- * @hide
- * @see com.android.server.usage.AppStandbyController
- */
- public static final String APP_IDLE_CONSTANTS = "app_idle_constants";
-
- /**
* Enable ART bytecode verification verifications for debuggable apps.
* 0 = disable, 1 = enable.
* @hide
@@ -14533,6 +14497,15 @@ public final class Settings {
public static final String SHOW_NEW_LOCKSCREEN = "show_new_lockscreen";
/**
+ * Whether to show new notification dismissal.
+ * Values are:
+ * 0: Disabled
+ * 1: Enabled
+ * @hide
+ */
+ public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss";
+
+ /**
* Block untrusted touches mode.
*
* Can be one of:
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index cc3e57859b64..d859b1c33c94 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -751,7 +751,7 @@ public class RecoveryController {
InternalRecoveryServiceException wrapUnexpectedServiceSpecificException(
ServiceSpecificException e) {
if (e.errorCode == ERROR_SERVICE_INTERNAL_ERROR) {
- return new InternalRecoveryServiceException(e.getMessage());
+ return new InternalRecoveryServiceException(e.getMessage(), e);
}
// Should never happen. If it does, it's a bug, and we need to update how the method that
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 18d79927388b..8ae1b6bf702d 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -20,7 +20,10 @@ import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.ClipData;
import android.content.IntentSender;
import android.os.Parcel;
import android.os.Parcelable;
@@ -97,7 +100,6 @@ import java.util.regex.Pattern;
* with the lower case value of the view's text are shown.
* <li>All other datasets are hidden.
* </ol>
- *
*/
public final class Dataset implements Parcelable {
@@ -106,6 +108,7 @@ public final class Dataset implements Parcelable {
private final ArrayList<RemoteViews> mFieldPresentations;
private final ArrayList<InlinePresentation> mFieldInlinePresentations;
private final ArrayList<DatasetFieldFilter> mFieldFilters;
+ @Nullable private final ClipData mFieldContent;
private final RemoteViews mPresentation;
@Nullable private final InlinePresentation mInlinePresentation;
private final IntentSender mAuthentication;
@@ -117,6 +120,7 @@ public final class Dataset implements Parcelable {
mFieldPresentations = builder.mFieldPresentations;
mFieldInlinePresentations = builder.mFieldInlinePresentations;
mFieldFilters = builder.mFieldFilters;
+ mFieldContent = builder.mFieldContent;
mPresentation = builder.mPresentation;
mInlinePresentation = builder.mInlinePresentation;
mAuthentication = builder.mAuthentication;
@@ -124,11 +128,15 @@ public final class Dataset implements Parcelable {
}
/** @hide */
+ @TestApi
+ @SuppressLint("ConcreteCollection")
public @Nullable ArrayList<AutofillId> getFieldIds() {
return mFieldIds;
}
/** @hide */
+ @TestApi
+ @SuppressLint("ConcreteCollection")
public @Nullable ArrayList<AutofillValue> getFieldValues() {
return mFieldValues;
}
@@ -140,24 +148,37 @@ public final class Dataset implements Parcelable {
}
/** @hide */
- @Nullable
- public InlinePresentation getFieldInlinePresentation(int index) {
+ public @Nullable InlinePresentation getFieldInlinePresentation(int index) {
final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index);
return inlinePresentation != null ? inlinePresentation : mInlinePresentation;
}
/** @hide */
- @Nullable
- public DatasetFieldFilter getFilter(int index) {
+ public @Nullable DatasetFieldFilter getFilter(int index) {
return mFieldFilters.get(index);
}
+ /**
+ * Returns the content to be filled for a non-text suggestion. This is only applicable to
+ * augmented autofill. The target field for the content is available via {@link #getFieldIds()}
+ * (guaranteed to have a single field id set when the return value here is non-null). See
+ * {@link Builder#setContent(AutofillId, ClipData)} for more info.
+ *
+ * @hide
+ */
+ @TestApi
+ public @Nullable ClipData getFieldContent() {
+ return mFieldContent;
+ }
+
/** @hide */
+ @TestApi
public @Nullable IntentSender getAuthentication() {
return mAuthentication;
}
/** @hide */
+ @TestApi
public boolean isEmpty() {
return mFieldIds == null || mFieldIds.isEmpty();
}
@@ -179,6 +200,9 @@ public final class Dataset implements Parcelable {
if (mFieldValues != null) {
builder.append(", fieldValues=").append(mFieldValues);
}
+ if (mFieldContent != null) {
+ builder.append(", fieldContent=").append(mFieldContent);
+ }
if (mFieldPresentations != null) {
builder.append(", fieldPresentations=").append(mFieldPresentations.size());
}
@@ -207,7 +231,8 @@ public final class Dataset implements Parcelable {
*
* @hide
*/
- public String getId() {
+ @TestApi
+ public @Nullable String getId() {
return mId;
}
@@ -221,6 +246,7 @@ public final class Dataset implements Parcelable {
private ArrayList<RemoteViews> mFieldPresentations;
private ArrayList<InlinePresentation> mFieldInlinePresentations;
private ArrayList<DatasetFieldFilter> mFieldFilters;
+ @Nullable private ClipData mFieldContent;
private RemoteViews mPresentation;
@Nullable private InlinePresentation mInlinePresentation;
private IntentSender mAuthentication;
@@ -366,6 +392,36 @@ public final class Dataset implements Parcelable {
}
/**
+ * Sets the content for a field.
+ *
+ * <p>Only called by augmented autofill.
+ *
+ * <p>For a given field, either a {@link AutofillValue value} or content can be filled, but
+ * not both. Furthermore, when filling content, only a single field can be filled.
+ *
+ * @param id id returned by
+ * {@link android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+ * @param content content to be autofilled. Pass {@code null} if you do not have the content
+ * but the target view is a logical part of the dataset. For example, if the dataset needs
+ * authentication.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
+ * @return this builder.
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder setContent(@NonNull AutofillId id, @Nullable ClipData content) {
+ throwIfDestroyed();
+ setLifeTheUniverseAndEverything(id, null, null, null, null);
+ mFieldContent = content;
+ return this;
+ }
+
+ /**
* Sets the value of a field.
*
* <b>Note:</b> Prior to Android {@link android.os.Build.VERSION_CODES#P}, this method would
@@ -659,6 +715,15 @@ public final class Dataset implements Parcelable {
if (mFieldIds == null) {
throw new IllegalStateException("at least one value must be set");
}
+ if (mFieldContent != null) {
+ if (mFieldIds.size() > 1) {
+ throw new IllegalStateException(
+ "when filling content, only one field can be filled");
+ }
+ if (mFieldValues.get(0) != null) {
+ throw new IllegalStateException("cannot fill both content and values");
+ }
+ }
return new Dataset(this);
}
@@ -687,6 +752,7 @@ public final class Dataset implements Parcelable {
parcel.writeTypedList(mFieldPresentations, flags);
parcel.writeTypedList(mFieldInlinePresentations, flags);
parcel.writeTypedList(mFieldFilters, flags);
+ parcel.writeParcelable(mFieldContent, flags);
parcel.writeParcelable(mAuthentication, flags);
parcel.writeString(mId);
}
@@ -694,18 +760,8 @@ public final class Dataset implements Parcelable {
public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
@Override
public Dataset createFromParcel(Parcel parcel) {
- // Always go through the builder to ensure the data ingested by
- // the system obeys the contract of the builder to avoid attacks
- // using specially crafted parcels.
final RemoteViews presentation = parcel.readParcelable(null);
final InlinePresentation inlinePresentation = parcel.readParcelable(null);
- final Builder builder = presentation != null
- ? inlinePresentation == null
- ? new Builder(presentation)
- : new Builder(presentation).setInlinePresentation(inlinePresentation)
- : inlinePresentation == null
- ? new Builder()
- : new Builder(inlinePresentation);
final ArrayList<AutofillId> ids =
parcel.createTypedArrayList(AutofillId.CREATOR);
final ArrayList<AutofillValue> values =
@@ -716,6 +772,21 @@ public final class Dataset implements Parcelable {
parcel.createTypedArrayList(InlinePresentation.CREATOR);
final ArrayList<DatasetFieldFilter> filters =
parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
+ final ClipData fieldContent = parcel.readParcelable(null);
+ final IntentSender authentication = parcel.readParcelable(null);
+ final String datasetId = parcel.readString();
+
+ // Always go through the builder to ensure the data ingested by
+ // the system obeys the contract of the builder to avoid attacks
+ // using specially crafted parcels.
+ final Builder builder = (presentation != null) ? new Builder(presentation)
+ : new Builder();
+ if (inlinePresentation != null) {
+ builder.setInlinePresentation(inlinePresentation);
+ }
+ if (fieldContent != null) {
+ builder.setContent(ids.get(0), fieldContent);
+ }
final int inlinePresentationsSize = inlinePresentations.size();
for (int i = 0; i < ids.size(); i++) {
final AutofillId id = ids.get(i);
@@ -727,8 +798,8 @@ public final class Dataset implements Parcelable {
builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation,
fieldInlinePresentation, filter);
}
- builder.setAuthentication(parcel.readParcelable(null));
- builder.setId(parcel.readString());
+ builder.setAuthentication(authentication);
+ builder.setId(datasetId);
return builder.build();
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 89e27ba21443..aa9e289345db 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1885,7 +1885,7 @@ public abstract class NotificationListenerService extends Service {
* <p>This might be null even if the notification is a conversation notification, if
* the posting app hasn't opted into the full conversation feature set yet.</p>
*/
- public @Nullable ShortcutInfo getShortcutInfo() {
+ public @Nullable ShortcutInfo getConversationShortcutInfo() {
return mShortcutInfo;
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 169507ce9871..787a81bac3c0 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -287,7 +287,7 @@ public class ZenModeConfig implements Parcelable {
}
StringBuilder buffer = new StringBuilder(automaticRules.size() * 28);
- buffer.append('{');
+ buffer.append("{\n");
for (int i = 0; i < automaticRules.size(); i++) {
if (i > 0) {
buffer.append(",\n");
@@ -1831,12 +1831,13 @@ public class ZenModeConfig implements Parcelable {
public String toString() {
return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
.append("id=").append(id)
+ .append(",state=").append(condition == null ? "STATE_FALSE"
+ : Condition.stateToString(condition.state))
.append(",enabled=").append(String.valueOf(enabled).toUpperCase())
.append(",snoozing=").append(snoozing)
.append(",name=").append(name)
.append(",zenMode=").append(Global.zenModeToString(zenMode))
.append(",conditionId=").append(conditionId)
- .append(",condition=").append(condition)
.append(",pkg=").append(pkg)
.append(",component=").append(component)
.append(",configActivity=").append(configurationActivity)
@@ -1844,6 +1845,7 @@ public class ZenModeConfig implements Parcelable {
.append(",enabler=").append(enabler)
.append(",zenPolicy=").append(zenPolicy)
.append(",modified=").append(modified)
+ .append(",condition=").append(condition)
.append(']').toString();
}
@@ -2011,6 +2013,10 @@ public class ZenModeConfig implements Parcelable {
public Diff addLine(String item, Object from, Object to) {
return addLine(item, from + "->" + to);
}
+
+ public boolean isEmpty() {
+ return lines.isEmpty();
+ }
}
/**
diff --git a/core/java/android/view/NotificationTopLineView.java b/core/java/android/view/NotificationTopLineView.java
index 9443ccfc7553..24748222b3af 100644
--- a/core/java/android/view/NotificationTopLineView.java
+++ b/core/java/android/view/NotificationTopLineView.java
@@ -19,6 +19,7 @@ package android.view;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.RemoteViews;
@@ -36,6 +37,7 @@ import java.util.List;
*/
@RemoteViews.RemoteView
public class NotificationTopLineView extends ViewGroup {
+ private final int mGravityY;
private final int mChildMinWidth;
private final int mContentEndMargin;
private View mAppName;
@@ -48,6 +50,9 @@ public class NotificationTopLineView extends ViewGroup {
private int mHeaderTextMarginEnd;
private List<View> mIconsAtEnd;
+ private int mMaxAscent;
+ private int mMaxDescent;
+
public NotificationTopLineView(Context context) {
this(context, null);
}
@@ -67,6 +72,20 @@ public class NotificationTopLineView extends ViewGroup {
Resources res = getResources();
mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
+
+ // NOTE: Implementation only supports TOP, BOTTOM, and CENTER_VERTICAL gravities,
+ // with CENTER_VERTICAL being the default.
+ int[] attrIds = {android.R.attr.gravity};
+ TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes);
+ int gravity = ta.getInt(0, 0);
+ ta.recycle();
+ if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
+ mGravityY = Gravity.BOTTOM;
+ } else if ((gravity & Gravity.TOP) == Gravity.TOP) {
+ mGravityY = Gravity.TOP;
+ } else {
+ mGravityY = Gravity.CENTER_VERTICAL;
+ }
}
@Override
@@ -84,12 +103,16 @@ public class NotificationTopLineView extends ViewGroup {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
+ final boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
MeasureSpec.AT_MOST);
int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
MeasureSpec.AT_MOST);
int totalWidth = getPaddingStart();
int iconWidth = getPaddingEnd();
+ int maxChildHeight = -1;
+ mMaxAscent = -1;
+ mMaxDescent = -1;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
@@ -108,6 +131,13 @@ public class NotificationTopLineView extends ViewGroup {
} else {
totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
}
+ int childBaseline = child.getBaseline();
+ int childHeight = child.getMeasuredHeight();
+ if (childBaseline != -1) {
+ mMaxAscent = Math.max(mMaxAscent, childBaseline);
+ mMaxDescent = Math.max(mMaxDescent, childHeight - childBaseline);
+ }
+ maxChildHeight = Math.max(maxChildHeight, childHeight);
}
// Ensure that there is at least enough space for the icons
@@ -125,7 +155,7 @@ public class NotificationTopLineView extends ViewGroup {
shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
0);
}
- setMeasuredDimension(givenWidth, givenHeight);
+ setMeasuredDimension(givenWidth, wrapHeight ? maxChildHeight : givenHeight);
}
private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
@@ -146,7 +176,13 @@ public class NotificationTopLineView extends ViewGroup {
int left = getPaddingStart();
int end = getMeasuredWidth();
int childCount = getChildCount();
- int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
+ int ownHeight = b - t;
+ int childSpace = ownHeight - mPaddingTop - mPaddingBottom;
+
+ // Instead of centering the baseline, pick a baseline that centers views which align to it.
+ // Only used when mGravityY is CENTER_VERTICAL
+ int baselineY = mPaddingTop + ((childSpace - (mMaxAscent + mMaxDescent)) / 2) + mMaxAscent;
+
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
@@ -156,8 +192,42 @@ public class NotificationTopLineView extends ViewGroup {
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
int layoutLeft;
int layoutRight;
- int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
- int bottom = top + childHeight;
+
+ // Calculate vertical alignment of the views, accounting for the view baselines
+ int childTop;
+ int childBaseline = child.getBaseline();
+ switch (mGravityY) {
+ case Gravity.TOP:
+ childTop = mPaddingTop + params.topMargin;
+ if (childBaseline != -1) {
+ childTop += mMaxAscent - childBaseline;
+ }
+ break;
+ case Gravity.CENTER_VERTICAL:
+ if (childBaseline != -1) {
+ // Align baselines vertically only if the child is smaller than us
+ if (childSpace - childHeight > 0) {
+ childTop = baselineY - childBaseline;
+ } else {
+ childTop = mPaddingTop + (childSpace - childHeight) / 2;
+ }
+ } else {
+ childTop = mPaddingTop + ((childSpace - childHeight) / 2)
+ + params.topMargin - params.bottomMargin;
+ }
+ break;
+ case Gravity.BOTTOM:
+ int childBottom = ownHeight - mPaddingBottom;
+ childTop = childBottom - childHeight - params.bottomMargin;
+ if (childBaseline != -1) {
+ int descent = childHeight - childBaseline;
+ childTop -= (mMaxDescent - descent);
+ }
+ break;
+ default:
+ childTop = mPaddingTop;
+ }
+
// Icons that should go at the end
if (mIconsAtEnd.contains(child)) {
if (end == getMeasuredWidth()) {
@@ -179,7 +249,7 @@ public class NotificationTopLineView extends ViewGroup {
layoutLeft = getWidth() - layoutRight;
layoutRight = getWidth() - ltrLeft;
}
- child.layout(layoutLeft, top, layoutRight, bottom);
+ child.layout(layoutLeft, childTop, layoutRight, childTop + childHeight);
}
updateTouchListener();
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5a4584387dba..3a15aa26e357 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -653,12 +653,14 @@ public final class SurfaceControl implements Parcelable {
private final Rect mSourceCrop = new Rect();
private final float mFrameScale;
private final boolean mCaptureSecureLayers;
+ private final boolean mAllowProtected;
private CaptureArgs(Builder<? extends Builder<?>> builder) {
mPixelFormat = builder.mPixelFormat;
mSourceCrop.set(builder.mSourceCrop);
mFrameScale = builder.mFrameScale;
mCaptureSecureLayers = builder.mCaptureSecureLayers;
+ mAllowProtected = builder.mAllowProtected;
}
/**
@@ -671,6 +673,7 @@ public final class SurfaceControl implements Parcelable {
private final Rect mSourceCrop = new Rect();
private float mFrameScale = 1;
private boolean mCaptureSecureLayers;
+ private boolean mAllowProtected;
/**
* The desired pixel format of the returned buffer.
@@ -709,6 +712,17 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Whether to allow the screenshot of protected (DRM) content. Warning: The screenshot
+ * cannot be read in unprotected space.
+ *
+ * @see HardwareBuffer#USAGE_PROTECTED_CONTENT
+ */
+ public T setAllowProtected(boolean allowProtected) {
+ mAllowProtected = allowProtected;
+ return getThis();
+ }
+
+ /**
* Each sub class should return itself to allow the builder to chain properly
*/
abstract T getThis();
@@ -1907,16 +1921,23 @@ public final class SurfaceControl implements Parcelable {
public float appRequestRefreshRateMin;
public float appRequestRefreshRateMax;
+ /**
+ * If true this will allow switching between modes in different display configuration
+ * groups. This way the user may see visual interruptions when the display mode changes.
+ */
+ public boolean allowGroupSwitching;
+
public DesiredDisplayConfigSpecs() {}
public DesiredDisplayConfigSpecs(DesiredDisplayConfigSpecs other) {
copyFrom(other);
}
- public DesiredDisplayConfigSpecs(int defaultConfig, float primaryRefreshRateMin,
- float primaryRefreshRateMax, float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+ public DesiredDisplayConfigSpecs(int defaultConfig, boolean allowGroupSwitching,
+ float primaryRefreshRateMin, float primaryRefreshRateMax,
+ float appRequestRefreshRateMin, float appRequestRefreshRateMax) {
this.defaultConfig = defaultConfig;
+ this.allowGroupSwitching = allowGroupSwitching;
this.primaryRefreshRateMin = primaryRefreshRateMin;
this.primaryRefreshRateMax = primaryRefreshRateMax;
this.appRequestRefreshRateMin = appRequestRefreshRateMin;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index fb66b5298839..81db62857c17 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -19,6 +19,7 @@ package android.view.autofill;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;
@@ -32,6 +33,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.AutofillOptions;
+import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -60,6 +62,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.KeyEvent;
+import android.view.OnReceiveContentCallback;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -2350,6 +2353,49 @@ public final class AutofillManager {
}
}
+ private void autofillContent(int sessionId, AutofillId id, ClipData clip) {
+ synchronized (mLock) {
+ if (sessionId != mSessionId) {
+ return;
+ }
+ final AutofillClient client = getClient();
+ if (client == null) {
+ return;
+ }
+ final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
+ if (view == null) {
+ // Most likely view has been removed after the initial request was sent to the
+ // the service; this is fine, but we need to update the view status in the
+ // server side so it can be triggered again.
+ Log.d(TAG, "autofillContent(): no view with id " + id);
+ reportAutofillContentFailure(id);
+ return;
+ }
+ OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL)
+ .build();
+ boolean handled = view.onReceiveContent(payload);
+ if (!handled) {
+ Log.w(TAG, "autofillContent(): receiver returned false: id=" + id
+ + ", view=" + view + ", clip=" + clip);
+ reportAutofillContentFailure(id);
+ return;
+ }
+ mMetricsLogger.write(newLog(MetricsEvent.AUTOFILL_DATASET_APPLIED)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, 1)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, 1));
+ }
+ }
+
+ private void reportAutofillContentFailure(AutofillId id) {
+ try {
+ mService.setAutofillFailure(mSessionId, Collections.singletonList(id),
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private LogMaker newLog(int category) {
final LogMaker log = new LogMaker(category)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, mSessionId);
@@ -3391,6 +3437,14 @@ public final class AutofillManager {
}
@Override
+ public void autofillContent(int sessionId, AutofillId id, ClipData content) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.autofillContent(sessionId, id, content));
+ }
+ }
+
+ @Override
public void authenticate(int sessionId, int authenticationId, IntentSender intent,
Intent fillInIntent, boolean authenticateInline) {
final AutofillManager afm = mAfm.get();
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index f8ccea5d8356..1f833f66c257 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -18,6 +18,7 @@ package android.view.autofill;
import java.util.List;
+import android.content.ClipData;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
@@ -48,6 +49,11 @@ oneway interface IAutoFillManagerClient {
boolean hideHighlight);
/**
+ * Autofills the activity with rich content data (e.g. an image) from a dataset.
+ */
+ void autofillContent(int sessionId, in AutofillId id, in ClipData content);
+
+ /**
* Authenticates a fill response or a data set.
*/
void authenticate(int sessionId, int authenticationId, in IntentSender intent,
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 093dfb4e196e..62b1b1f8cf53 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -502,7 +502,10 @@ public class BaseInputConnection implements InputConnection {
* The default implementation returns the given amount of text from the
* current cursor position in the buffer.
*/
- public CharSequence getTextBeforeCursor(int length, int flags) {
+ @Nullable
+ public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
+ if (length < 0) return null;
+
final Editable content = getEditable();
if (content == null) return null;
@@ -558,7 +561,10 @@ public class BaseInputConnection implements InputConnection {
* The default implementation returns the given amount of text from the
* current cursor position in the buffer.
*/
- public CharSequence getTextAfterCursor(int length, int flags) {
+ @Nullable
+ public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
+ if (length < 0) return null;
+
final Editable content = getEditable();
if (content == null) return null;
@@ -594,6 +600,8 @@ public class BaseInputConnection implements InputConnection {
@Nullable
public SurroundingText getSurroundingText(
@IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+ if (beforeLength < 0 || afterLength < 0) return null;
+
final Editable content = getEditable();
if (content == null) return null;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index c7acd298cd20..0fe47b7828d9 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -27,6 +27,8 @@ import android.text.TextUtils;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -186,13 +188,15 @@ public interface InputConnection {
* the current line, and specifically do not return 0 characters unless
* the cursor is really at the start of the text.</p>
*
- * @param n The expected length of the text.
+ * @param n The expected length of the text. This must be non-negative.
* @param flags Supplies additional options controlling how the text is
* returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
* @return the text before the cursor position; the length of the
* returned text might be less than <var>n</var>.
+ * @throws IllegalArgumentException if {@code n} is negative.
*/
- CharSequence getTextBeforeCursor(int n, int flags);
+ @Nullable
+ CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags);
/**
* Get <var>n</var> characters of text after the current cursor
@@ -228,14 +232,16 @@ public interface InputConnection {
* the current line, and specifically do not return 0 characters unless
* the cursor is really at the end of the text.</p>
*
- * @param n The expected length of the text.
+ * @param n The expected length of the text. This must be non-negative.
* @param flags Supplies additional options controlling how the text is
* returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
*
* @return the text after the cursor position; the length of the
* returned text might be less than <var>n</var>.
+ * @throws IllegalArgumentException if {@code n} is negative.
*/
- CharSequence getTextAfterCursor(int n, int flags);
+ @Nullable
+ CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags);
/**
* Gets the selected text, if any.
@@ -307,11 +313,15 @@ public interface InputConnection {
* editor can't comply with the request for some reason, or the application does not implement
* this method. The length of the returned text might be less than the sum of
* <var>beforeLength</var> and <var>afterLength</var> .
+ * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative.
*/
@Nullable
default SurroundingText getSurroundingText(
@IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength,
@GetTextType int flags) {
+ Preconditions.checkArgumentNonnegative(beforeLength);
+ Preconditions.checkArgumentNonnegative(afterLength);
+
CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags);
if (textBeforeCursor == null) {
textBeforeCursor = "";
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index ec7fa60a62ba..77956d15e399 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -16,11 +16,14 @@
package android.view.inputmethod;
+import android.annotation.IntRange;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
+import com.android.internal.util.Preconditions;
+
/**
* <p>Wrapper class for proxying calls to another InputConnection. Subclass and have fun!
*/
@@ -74,18 +77,24 @@ public class InputConnectionWrapper implements InputConnection {
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
+ * @throws IllegalArgumentException if {@code length} is negative.
*/
+ @Nullable
@Override
- public CharSequence getTextBeforeCursor(int n, int flags) {
+ public CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags) {
+ Preconditions.checkArgumentNonnegative(n);
return mTarget.getTextBeforeCursor(n, flags);
}
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
+ * @throws IllegalArgumentException if {@code length} is negative.
*/
+ @Nullable
@Override
- public CharSequence getTextAfterCursor(int n, int flags) {
+ public CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags) {
+ Preconditions.checkArgumentNonnegative(n);
return mTarget.getTextAfterCursor(n, flags);
}
@@ -101,10 +110,13 @@ public class InputConnectionWrapper implements InputConnection {
/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
+ * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative.
*/
@Nullable
@Override
public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ Preconditions.checkArgumentNonnegative(beforeLength);
+ Preconditions.checkArgumentNonnegative(afterLength);
return mTarget.getSurroundingText(beforeLength, afterLength, flags);
}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index ab6dcb1b27a7..7db35d4bf8b5 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -318,7 +318,8 @@ public final class TextClassification implements Parcelable {
public static PendingIntent createPendingIntent(
@NonNull final Context context, @NonNull final Intent intent, int requestCode) {
return PendingIntent.getActivity(
- context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ context, requestCode, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
/**
diff --git a/packages/Tethering/src/android/net/util/TetheringMessageBase.java b/core/java/android/window/DisplayAreaAppearedInfo.aidl
index 29c0a817b6f4..365f3e56d968 100644
--- a/packages/Tethering/src/android/net/util/TetheringMessageBase.java
+++ b/core/java/android/window/DisplayAreaAppearedInfo.aidl
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
+
+package android.window;
/**
- * This class defines Message.what base addresses for various state machine.
+ * Data object for the DisplayArea info provided when a DisplayArea is presented to an organizer.
+ *
+ * @hide
*/
-public class TetheringMessageBase {
- public static final int BASE_MAIN_SM = 0;
- public static final int BASE_IPSERVER = 100;
-
-}
+parcelable DisplayAreaAppearedInfo;
diff --git a/core/java/android/window/DisplayAreaAppearedInfo.java b/core/java/android/window/DisplayAreaAppearedInfo.java
new file mode 100644
index 000000000000..d33d77d54031
--- /dev/null
+++ b/core/java/android/window/DisplayAreaAppearedInfo.java
@@ -0,0 +1,88 @@
+/*
+ * 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.window;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+/**
+ * Data object for the DisplayArea info provided when a DisplayArea is presented to an organizer.
+ *
+ * @hide
+ */
+@TestApi
+public final class DisplayAreaAppearedInfo implements Parcelable {
+
+ @NonNull
+ private final DisplayAreaInfo mDisplayAreaInfo;
+
+ @NonNull
+ private final SurfaceControl mLeash;
+
+ @NonNull
+ public static final Creator<DisplayAreaAppearedInfo> CREATOR =
+ new Creator<DisplayAreaAppearedInfo>() {
+ @Override
+ public DisplayAreaAppearedInfo createFromParcel(Parcel source) {
+ final DisplayAreaInfo displayAreaInfo = source.readTypedObject(DisplayAreaInfo.CREATOR);
+ final SurfaceControl leash = source.readTypedObject(SurfaceControl.CREATOR);
+ return new DisplayAreaAppearedInfo(displayAreaInfo, leash);
+ }
+
+ @Override
+ public DisplayAreaAppearedInfo[] newArray(int size) {
+ return new DisplayAreaAppearedInfo[size];
+ }
+
+ };
+
+ public DisplayAreaAppearedInfo(@NonNull DisplayAreaInfo displayAreaInfo,
+ @NonNull SurfaceControl leash) {
+ mDisplayAreaInfo = displayAreaInfo;
+ mLeash = leash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mDisplayAreaInfo, flags);
+ dest.writeTypedObject(mLeash, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @return the DisplayArea info.
+ */
+ @NonNull
+ public DisplayAreaInfo getDisplayAreaInfo() {
+ return mDisplayAreaInfo;
+ }
+
+ /**
+ * @return the leash for the DisplayArea.
+ */
+ @NonNull
+ public SurfaceControl getLeash() {
+ return mLeash;
+ }
+}
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 38b2190a57f3..6ec093e045fa 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -16,12 +16,15 @@
package android.window;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.os.RemoteException;
import android.view.SurfaceControl;
+import java.util.List;
+
/**
* Interface for WindowManager to delegate control of display areas.
* @hide
@@ -84,10 +87,17 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
*/
public static final int FEATURE_VENDOR_FIRST = FEATURE_SYSTEM_LAST + 1;
+ /**
+ * Registers a DisplayAreaOrganizer to manage display areas for a given feature.
+ *
+ * @return a list of display areas that should be managed by the organizer.
+ */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
- public void registerOrganizer(int displayAreaFeature) {
+ @CallSuper
+ @NonNull
+ public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
try {
- getController().registerOrganizer(mInterface, displayAreaFeature);
+ return getController().registerOrganizer(mInterface, displayAreaFeature).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -97,6 +107,7 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
* @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ @CallSuper
public void unregisterOrganizer() {
try {
getController().unregisterOrganizer(mInterface);
@@ -105,6 +116,11 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
}
}
+ /**
+ * Called when a DisplayArea of the registered window type can be controlled by this organizer.
+ * It will not be called for the DisplayAreas that exist when {@link #registerOrganizer(int)} is
+ * called.
+ */
public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
@NonNull SurfaceControl leash) {}
diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl
index 41b9d027344e..8943847073c7 100644
--- a/core/java/android/window/IDisplayAreaOrganizerController.aidl
+++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl
@@ -16,13 +16,20 @@
package android.window;
+import android.content.pm.ParceledListSlice;
+import android.window.DisplayAreaAppearedInfo;
import android.window.IDisplayAreaOrganizer;
/** @hide */
interface IDisplayAreaOrganizerController {
- /** Register a DisplayAreaOrganizer to manage display areas for a given feature. */
- void registerOrganizer(in IDisplayAreaOrganizer organizer, int displayAreaFeature);
+ /**
+ * Registers a DisplayAreaOrganizer to manage display areas for a given feature.
+ *
+ * @return a list of display areas that should be managed by the organizer.
+ */
+ ParceledListSlice<DisplayAreaAppearedInfo> registerOrganizer(in IDisplayAreaOrganizer organizer,
+ int displayAreaFeature);
/**
* Unregisters a previously registered display area organizer.
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 52dc7e646d29..762297d15e6d 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -193,7 +193,7 @@ public class SuspendedAppActivity extends AlertActivity
try {
mSuspendingAppResources = createContextAsUser(
UserHandle.of(mUserId), /* flags */ 0).getPackageManager()
- .getResourcesForApplication(mSuspendedPackage);
+ .getResourcesForApplication(mSuspendingPackage);
} catch (PackageManager.NameNotFoundException ne) {
Slog.e(TAG, "Could not find resources for " + mSuspendingPackage, ne);
}
diff --git a/core/java/com/android/internal/app/TEST_MAPPING b/core/java/com/android/internal/app/TEST_MAPPING
index 373a5d9413a5..8bd791297e9d 100644
--- a/core/java/com/android/internal/app/TEST_MAPPING
+++ b/core/java/com/android/internal/app/TEST_MAPPING
@@ -1,4 +1,10 @@
{
+ "presubmit": [
+ {
+ "name": "CtsSuspendAppsTestCases",
+ "file_patterns": ["(/|^)SuspendedAppActivity\\.java"]
+ }
+ ],
"postsubmit": [
{
"name": "FrameworksCoreTests",
@@ -17,4 +23,4 @@
]
}
]
-} \ No newline at end of file
+}
diff --git a/core/java/com/android/internal/net/LegacyVpnInfo.java b/core/java/com/android/internal/net/LegacyVpnInfo.java
index 4eb7dede7769..43984b59378c 100644
--- a/core/java/com/android/internal/net/LegacyVpnInfo.java
+++ b/core/java/com/android/internal/net/LegacyVpnInfo.java
@@ -83,8 +83,8 @@ public class LegacyVpnInfo implements Parcelable {
* Return best matching {@link LegacyVpnInfo} state based on given
* {@link NetworkInfo}.
*/
- public static int stateFromNetworkInfo(NetworkInfo info) {
- switch (info.getDetailedState()) {
+ public static int stateFromNetworkInfo(NetworkInfo.DetailedState state) {
+ switch (state) {
case CONNECTING:
return STATE_CONNECTING;
case CONNECTED:
@@ -94,8 +94,7 @@ public class LegacyVpnInfo implements Parcelable {
case FAILED:
return STATE_FAILED;
default:
- Log.w(TAG, "Unhandled state " + info.getDetailedState()
- + " ; treating as disconnected");
+ Log.w(TAG, "Unhandled state " + state + " ; treating as disconnected");
return STATE_DISCONNECTED;
}
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e848da9b8ee3..f89e52d4f91d 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2540,6 +2540,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
+ params.backgroundBlurRadius = a.getDimensionPixelSize(
+ R.styleable.Window_windowBackgroundBlurRadius, 0);
+
if (params.windowAnimations == 0) {
params.windowAnimations = a.getResourceId(
R.styleable.Window_windowAnimationStyle, 0);
diff --git a/core/java/com/android/internal/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/common/ProtoLog.java
index ab58d351d3b9..01cc1ed37ad4 100644
--- a/core/java/com/android/internal/protolog/common/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/common/ProtoLog.java
@@ -35,6 +35,10 @@ package com.android.internal.protolog.common;
* during build.
*/
public class ProtoLog {
+
+ // Needs to be set directly otherwise the protologtool tries to transform the method call
+ public static boolean REQUIRE_PROTOLOGTOOL = true;
+
/**
* DEBUG level log.
*
@@ -44,8 +48,10 @@ public class ProtoLog {
*/
public static void d(IProtoLogGroup group, String messageString, Object... args) {
// Stub, replaced by the ProtoLogTool.
- throw new UnsupportedOperationException(
- "ProtoLog calls MUST be processed with ProtoLogTool");
+ if (REQUIRE_PROTOLOGTOOL) {
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
}
/**
@@ -57,8 +63,10 @@ public class ProtoLog {
*/
public static void v(IProtoLogGroup group, String messageString, Object... args) {
// Stub, replaced by the ProtoLogTool.
- throw new UnsupportedOperationException(
- "ProtoLog calls MUST be processed with ProtoLogTool");
+ if (REQUIRE_PROTOLOGTOOL) {
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
}
/**
@@ -70,8 +78,10 @@ public class ProtoLog {
*/
public static void i(IProtoLogGroup group, String messageString, Object... args) {
// Stub, replaced by the ProtoLogTool.
- throw new UnsupportedOperationException(
- "ProtoLog calls MUST be processed with ProtoLogTool");
+ if (REQUIRE_PROTOLOGTOOL) {
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
}
/**
@@ -83,8 +93,10 @@ public class ProtoLog {
*/
public static void w(IProtoLogGroup group, String messageString, Object... args) {
// Stub, replaced by the ProtoLogTool.
- throw new UnsupportedOperationException(
- "ProtoLog calls MUST be processed with ProtoLogTool");
+ if (REQUIRE_PROTOLOGTOOL) {
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
}
/**
@@ -96,8 +108,10 @@ public class ProtoLog {
*/
public static void e(IProtoLogGroup group, String messageString, Object... args) {
// Stub, replaced by the ProtoLogTool.
- throw new UnsupportedOperationException(
- "ProtoLog calls MUST be processed with ProtoLogTool");
+ if (REQUIRE_PROTOLOGTOOL) {
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
}
/**
@@ -109,7 +123,9 @@ public class ProtoLog {
*/
public static void wtf(IProtoLogGroup group, String messageString, Object... args) {
// Stub, replaced by the ProtoLogTool.
- throw new UnsupportedOperationException(
- "ProtoLog calls MUST be processed with ProtoLogTool");
+ if (REQUIRE_PROTOLOGTOOL) {
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
}
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index f086dd79758b..e05aa8351681 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -17,6 +17,7 @@
package com.android.internal.view;
import android.annotation.AnyThread;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.inputmethodservice.AbstractInputMethodService;
@@ -106,9 +107,13 @@ public class InputConnectionWrapper implements InputConnection {
return null;
}
+ /**
+ * See {@link InputConnection#getTextAfterCursor(int, int)}.
+ */
+ @Nullable
@AnyThread
- public CharSequence getTextAfterCursor(int length, int flags) {
- if (mCancellationGroup.isCanceled()) {
+ public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
+ if (length < 0 || mCancellationGroup.isCanceled()) {
return null;
}
@@ -122,9 +127,13 @@ public class InputConnectionWrapper implements InputConnection {
return getResultOrNull(value, "getTextAfterCursor()");
}
+ /**
+ * See {@link InputConnection#getTextBeforeCursor(int, int)}.
+ */
+ @Nullable
@AnyThread
- public CharSequence getTextBeforeCursor(int length, int flags) {
- if (mCancellationGroup.isCanceled()) {
+ public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
+ if (length < 0 || mCancellationGroup.isCanceled()) {
return null;
}
@@ -171,10 +180,12 @@ public class InputConnectionWrapper implements InputConnection {
* not support this protocol.
*/
@AnyThread
- public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
- if (mCancellationGroup.isCanceled()) {
+ public SurroundingText getSurroundingText(
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+ if (beforeLength < 0 || afterLength < 0 || mCancellationGroup.isCanceled()) {
return null;
}
+
if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
// This method is not implemented.
return null;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 4f97975838d5..661bbed8abcb 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -1,4 +1,3 @@
-
cc_library_shared {
name: "libandroid_runtime",
host_supported: true,
@@ -286,8 +285,9 @@ cc_library_shared {
],
include_dirs: [
"external/vulkan-headers/include",
+ "frameworks/native/libs/math/include",
"frameworks/native/libs/nativebase/include",
- "frameworks/native/libs/nativewindow/include"
+ "frameworks/native/libs/nativewindow/include",
],
shared_libs: [
"libicui18n",
@@ -303,10 +303,36 @@ cc_library_shared {
linux_glibc: {
srcs: [
"android_content_res_ApkAssets.cpp",
+ "android_hardware_input_InputApplicationHandle.cpp",
"android_os_MessageQueue.cpp",
+ "android_os_Parcel.cpp",
+
+ "android_view_KeyCharacterMap.cpp",
+ "android_view_KeyEvent.cpp",
+ "android_view_InputChannel.cpp",
+ "android_view_InputDevice.cpp",
+ "android_view_InputEventReceiver.cpp",
+ "android_view_InputEventSender.cpp",
+ "android_view_MotionEvent.cpp",
+ "android_view_VelocityTracker.cpp",
+ "android_view_VerifiedKeyEvent.cpp",
+ "android_view_VerifiedMotionEvent.cpp",
+
"android_util_AssetManager.cpp",
+ "android_util_Binder.cpp",
+
"android_util_FileObserver.cpp",
],
+ static_libs: [
+ "libinput",
+ "libbinderthreadstateutils",
+ ],
+ shared_libs: [
+ // libbinder needs to be shared since it has global state
+ // (e.g. gDefaultServiceManager)
+ "libbinder",
+ "libhidlbase", // libhwbinder is in here
+ ],
},
windows: {
enabled: true,
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 32b8fa61c8a0..a3cb4c076593 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -40,6 +40,7 @@
#include <binder/Stability.h>
#include <binderthreadstate/CallerUtils.h>
#include <cutils/atomic.h>
+#include <cutils/threads.h>
#include <log/log.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 62f844eb59a8..94151b522c9c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -110,6 +110,7 @@ static struct {
jfieldID sourceCrop;
jfieldID frameScale;
jfieldID captureSecureLayers;
+ jfieldID allowProtected;
} gCaptureArgsClassInfo;
static struct {
@@ -196,6 +197,7 @@ static struct {
jclass clazz;
jmethodID ctor;
jfieldID defaultConfig;
+ jfieldID allowGroupSwitching;
jfieldID primaryRefreshRateMin;
jfieldID primaryRefreshRateMax;
jfieldID appRequestRefreshRateMin;
@@ -367,6 +369,8 @@ static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs&
env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScale);
captureArgs.captureSecureLayers =
env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers);
+ captureArgs.allowProtected =
+ env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected);
}
static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
@@ -1003,6 +1007,9 @@ static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jo
jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs,
gDesiredDisplayConfigSpecsClassInfo.defaultConfig);
+ jboolean allowGroupSwitching =
+ env->GetBooleanField(desiredDisplayConfigSpecs,
+ gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching);
jfloat primaryRefreshRateMin =
env->GetFloatField(desiredDisplayConfigSpecs,
gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin);
@@ -1017,6 +1024,7 @@ static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jo
gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax);
size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(token, defaultConfig,
+ allowGroupSwitching,
primaryRefreshRateMin,
primaryRefreshRateMax,
appRequestRefreshRateMin,
@@ -1029,11 +1037,13 @@ static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, job
if (token == nullptr) return nullptr;
int32_t defaultConfig;
+ bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
float appRequestRefreshRateMin;
float appRequestRefreshRateMax;
if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig,
+ &allowGroupSwitching,
&primaryRefreshRateMin,
&primaryRefreshRateMax,
&appRequestRefreshRateMin,
@@ -1044,8 +1054,8 @@ static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, job
return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz,
gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig,
- primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ allowGroupSwitching, primaryRefreshRateMin, primaryRefreshRateMax,
+ appRequestRefreshRateMin, appRequestRefreshRateMax);
}
static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
@@ -1862,9 +1872,11 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gDesiredDisplayConfigSpecsClassInfo.clazz =
MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz);
gDesiredDisplayConfigSpecsClassInfo.ctor =
- GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IFFFF)V");
+ GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
gDesiredDisplayConfigSpecsClassInfo.defaultConfig =
GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I");
+ gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching =
+ GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "allowGroupSwitching", "Z");
gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin =
GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMin", "F");
gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax =
@@ -1881,6 +1893,8 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gCaptureArgsClassInfo.frameScale = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScale", "F");
gCaptureArgsClassInfo.captureSecureLayers =
GetFieldIDOrDie(env, captureArgsClazz, "mCaptureSecureLayers", "Z");
+ gCaptureArgsClassInfo.allowProtected =
+ GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z");
jclass displayCaptureArgsClazz =
FindClassOrDie(env, "android/view/SurfaceControl$DisplayCaptureArgs");
diff --git a/core/proto/android/providers/settings/config.proto b/core/proto/android/providers/settings/config.proto
index f343c3a30927..c5ea2ab0b868 100644
--- a/core/proto/android/providers/settings/config.proto
+++ b/core/proto/android/providers/settings/config.proto
@@ -31,6 +31,7 @@ message ConfigSettingsProto {
repeated SettingProto activity_manager_settings = 4;
repeated SettingProto alarm_manager_settings = 26;
repeated SettingProto app_compat_settings = 5;
+ repeated SettingProto app_standby_settings = 27;
repeated SettingProto autofill_settings = 6;
repeated SettingProto blobstore_settings = 23;
repeated SettingProto connectivity_settings = 7;
@@ -52,7 +53,7 @@ message ConfigSettingsProto {
repeated SettingProto telephony_settings = 21;
repeated SettingProto textclassifier_settings = 22;
- // Next tag: 27
+ // Next tag: 28
message NamespaceProto {
optional string namespace = 1;
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 9291a90574cd..1ea1bafd4d2f 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -78,7 +78,7 @@ message GlobalSettingsProto {
option (android.msg_privacy).dest = DEST_EXPLICIT;
// These are key=value lists, separated by commas.
- optional SettingProto idle_constants = 1;
+ reserved 1; // idle_constants
optional SettingProto standby_enabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto auto_restriction_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto forced_app_standby_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 221bcf6a800c..34c7fa734f21 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -39,10 +39,6 @@
android:layout_height="@dimen/notification_progress_bar_height"
android:layout_marginTop="@dimen/notification_progress_margin_top"
layout="@layout/notification_template_progress" />
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_content_margin" />
</LinearLayout>
<include layout="@layout/notification_template_right_icon" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_top_line.xml b/core/res/res/layout/notification_template_top_line.xml
index 27fab859a045..0786e138559f 100644
--- a/core/res/res/layout/notification_template_top_line.xml
+++ b/core/res/res/layout/notification_template_top_line.xml
@@ -102,9 +102,8 @@
android:id="@+id/alerted_icon"
android:layout_width="@dimen/notification_alerted_size"
android:layout_height="@dimen/notification_alerted_size"
- android:layout_gravity="center"
android:layout_marginStart="4dp"
- android:paddingTop="1dp"
+ android:baseline="10dp"
android:scaleType="fitCenter"
android:visibility="gone"
android:contentDescription="@string/notification_alerted_content_description"
@@ -116,8 +115,7 @@
android:layout_height="@dimen/notification_feedback_size"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
- android:paddingTop="2dp"
- android:layout_gravity="center"
+ android:baseline="10dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_feedback_indicator"
android:background="?android:selectableItemBackgroundBorderless"
@@ -128,9 +126,8 @@
android:id="@+id/profile_badge"
android:layout_width="@dimen/notification_badge_size"
android:layout_height="@dimen/notification_badge_size"
- android:layout_gravity="center"
android:layout_marginStart="4dp"
- android:paddingTop="1dp"
+ android:baseline="10dp"
android:scaleType="fitCenter"
android:visibility="gone"
android:contentDescription="@string/notification_work_profile_content_description"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b74f96de33b1..03e64a4038ad 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2181,6 +2181,10 @@
the decor view. -->
<attr name="windowLightNavigationBar" format="boolean" />
+ <!-- @hide -->
+ <attr name="windowBackgroundBlurRadius" format="dimension"/>
+
+
<!-- Controls how the window is laid out if there is a {@code DisplayCutout}.
<p>
Defaults to {@code default}.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 68fd7c71bcfd..df03ccc784d4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3358,6 +3358,10 @@
ratio larger than this is considered to wide and short to be usable. Currently 2.39:1. -->
<item name="config_pictureInPictureMaxAspectRatio" format="float" type="dimen">2.39</item>
+ <!-- The maximum number of actions that is supported for picture-in-picture. This number
+ must be no less than 3 for back compatibility. -->
+ <integer name="config_pictureInPictureMaxNumberOfActions">3</integer>
+
<!-- Controls the snap mode for the docked stack divider
0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio
1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 39989dd24a23..1e9a747d30dc 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3047,6 +3047,8 @@
<public name="rollbackDataPolicy" />
<public name="allowClickWhenDisabled" />
<public name="windowLayoutAffinity" />
+ <!-- @hide -->
+ <public name="windowBackgroundBlurRadius"/>
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8daa6539ff64..a716875b068f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -409,6 +409,7 @@
<java-symbol type="integer" name="config_defaultPictureInPictureGravity" />
<java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" />
<java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" />
+ <java-symbol type="integer" name="config_pictureInPictureMaxNumberOfActions" />
<java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" />
<java-symbol type="integer" name="config_bluetooth_max_advertisers" />
<java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
diff --git a/core/tests/BTtraffic/Android.bp b/core/tests/BTtraffic/Android.bp
deleted file mode 100644
index e508570daf03..000000000000
--- a/core/tests/BTtraffic/Android.bp
+++ /dev/null
@@ -1,7 +0,0 @@
-android_app {
- name: "bttraffic",
- srcs: ["src/**/*.java"],
- resource_dirs: ["res"],
- sdk_version: "current",
- certificate: "platform",
-}
diff --git a/core/tests/BTtraffic/AndroidManifest.xml b/core/tests/BTtraffic/AndroidManifest.xml
deleted file mode 100644
index 00d9707de2bf..000000000000
--- a/core/tests/BTtraffic/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.experimental.bttraffic" >
-
- <uses-permission android:name="android.permission.BLUETOOTH"/>
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
-
- <uses-sdk
- android:minSdkVersion="18"
- android:targetSdkVersion="18"
- />
- <application
- android:allowBackup="false"
- android:label="@string/app_name" >
- <service
- android:name=".BTtraffic"
- android:enabled="true"
- android:exported="true" >
- </service>
- </application>
-
-</manifest>
diff --git a/core/tests/BTtraffic/README b/core/tests/BTtraffic/README
deleted file mode 100644
index 430488f656f9..000000000000
--- a/core/tests/BTtraffic/README
+++ /dev/null
@@ -1,45 +0,0 @@
-This is a tool to generate classic Bluetooth traffic with specified period and package size.
-Together with the SvcMonitor, which will be called automatically in this android service, can be
-used to measure the CPU usage from the Java layer Bluetooth code and the underlying system service
-com.android.bluetooth.
-
-1. Server (Listener) - Client (Sender) model. Both run as an Android service.
-2. No pairing needed. Communicate via unsecured RFcomm. Client establishes the connection by
-providing the MAC addr of the server.
-3. Bluetooth has to be turned on on both side.
-4. Client can configure the traffic by specifying the transfer period and package size.
-5. A separate monitor process will be automatically forked and will be reading from /proc file
-system to calculate the cpu usage. The measurement is updated once per second.
-6. The monitor process (com.google.android.experimental.svcmonitor/.ScvMonitor) can be run as an
-independent service to measure cpu usage on any similarly configured tests (e.g. wifi, BLE). Refer
-to SvcMonitor's README for usage and details.
-
-Usage:
-To instal the test:
-On both the server and client device, install the 2 apk:
-$ adb install $OUT/system/app/bttraffic/bttraffic.apk
-$ adb install $OUT/system/app/svcmonitor/svcmonitor.apk
-
-To start the service on the SERVER side:
-$ adb shell am startservice -a start --ez ack true \
-com.google.android.experimental.bttraffic/.BTtraffic
-
-To start the service on the CLIENT side:
-$ adb shell am startservice -a start \
--e addr "F8:A9:D0:A8:74:8E" --ei size 1000 --ei period 15 \
-com.google.android.experimental.bttraffic/.BTtraffic
-
-To stop the test:
-On either the server or client:
-$ adb shell am startservice -a stop \
-com.google.android.experimental.bttraffic/.BTtraffic
-
-To look at the data:
-$ adb logcat | grep bttraffic
-
-Options:
--e addr: MAC addr of the server, in uppercase letter.
---ei size: package size, unit: byte; default: 1024, MAX: 20MB
---ei period: system sleep time between sending each package, unit: ms, default: 5000
- ** if -1 is provided, client will only send the package once.
---ez ack: whether acknowledge is required (true/false)
diff --git a/core/tests/BTtraffic/res/values/strings.xml b/core/tests/BTtraffic/res/values/strings.xml
deleted file mode 100644
index e70276e03647..000000000000
--- a/core/tests/BTtraffic/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
- <string name="app_name">Bluetooth Test</string>
-</resources>
diff --git a/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java
deleted file mode 100644
index 286c0aa2915f..000000000000
--- a/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java
+++ /dev/null
@@ -1,328 +0,0 @@
-package com.google.android.experimental.bttraffic;
-
-import android.app.Service;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothServerSocket;
-import android.bluetooth.BluetoothSocket;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.Exception;
-import java.lang.Runtime;
-import java.lang.RuntimeException;
-import java.lang.Process;
-import java.nio.ByteBuffer;
-import java.util.Random;
-import java.util.Set;
-import java.util.UUID;
-
-public class BTtraffic extends Service {
- public static final String TAG = "bttraffic";
- static final String SERVICE_NAME = "bttraffic";
- static final String SYS_SERVICE_NAME = "com.android.bluetooth";
- static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67");
- volatile Thread mWorkerThread;
- volatile boolean isShuttingDown = false;
- volatile boolean isServer = false;
-
- public BTtraffic() {}
-
- static void safeClose(Closeable closeable) {
- try {
- closeable.close();
- } catch (IOException e) {
- Log.d(TAG, "Unable to close resource.\n");
- }
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent == null) {
- stopSelf();
- return 0;
- }
- if ("stop".equals(intent.getAction())) {
- stopService();
- } else if ("start".equals(intent.getAction())) {
- startWorker(intent);
- } else {
- Log.d(TAG, "unknown action: + " + intent.getAction());
- }
- return 0;
- }
-
- private void startWorker(Intent intent) {
- if (mWorkerThread != null) {
- Log.d(TAG, "worker thread already active");
- return;
- }
- isShuttingDown = false;
- String remoteAddr = intent.getStringExtra("addr");
- Log.d(TAG, "startWorker: addr=" + remoteAddr);
- Runnable worker =
- remoteAddr == null
- ? new ListenerRunnable(this, intent)
- : new SenderRunnable(this, remoteAddr, intent);
- isServer = remoteAddr == null ? true: false;
- mWorkerThread = new Thread(worker, "BTtrafficWorker");
- try {
- startMonitor();
- Log.d(TAG, "Monitor service started");
- mWorkerThread.start();
- Log.d(TAG, "Worker thread started");
- } catch (Exception e) {
- Log.d(TAG, "Failed to start service", e);
- }
- }
-
- private void startMonitor()
- throws Exception {
- if (isServer) {
- Log.d(TAG, "Start monitor on server");
- String[] startmonitorCmd = {
- "/system/bin/am",
- "startservice",
- "-a", "start",
- "-e", "java", SERVICE_NAME,
- "-e", "hal", SYS_SERVICE_NAME,
- "com.google.android.experimental.svcmonitor/.SvcMonitor"
- };
- Process ps = new ProcessBuilder()
- .command(startmonitorCmd)
- .redirectErrorStream(true)
- .start();
- } else {
- Log.d(TAG, "No need to start SvcMonitor on client");
- }
- }
-
- private void stopMonitor()
- throws Exception {
- if (isServer) {
- Log.d(TAG, "StopMonitor on server");
- String[] stopmonitorCmd = {
- "/system/bin/am",
- "startservice",
- "-a", "stop",
- "com.google.android.experimental.svcmonitor/.SvcMonitor"
- };
- Process ps = new ProcessBuilder()
- .command(stopmonitorCmd)
- .redirectErrorStream(true)
- .start();
- } else {
- Log.d(TAG, "No need to stop Svcmonitor on client");
- }
- }
-
- public void stopService() {
- if (mWorkerThread == null) {
- Log.d(TAG, "no active thread");
- return;
- }
-
- isShuttingDown = true;
-
- try {
- stopMonitor();
- } catch (Exception e) {
- Log.d(TAG, "Unable to stop SvcMonitor!", e);
- }
-
- if (Thread.currentThread() != mWorkerThread) {
- mWorkerThread.interrupt();
- Log.d(TAG, "Interrupting thread");
- try {
- mWorkerThread.join();
- } catch (InterruptedException e) {
- Log.d(TAG, "Unable to join thread!");
- }
- }
-
- mWorkerThread = null;
- stopSelf();
- Log.d(TAG, "Service stopped");
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- throw new UnsupportedOperationException("Not yet implemented");
- }
-
- public static class ListenerRunnable implements Runnable {
- private final BTtraffic bttraffic;
- private final boolean sendAck;
- private Intent intent;
- private final int maxbuffersize = 20 * 1024 * 1024;
-
- public ListenerRunnable(BTtraffic bttraffic, Intent intent) {
- this.bttraffic = bttraffic;
- this.sendAck = intent.getBooleanExtra("ack", true);
- this.intent = intent;
- }
-
- @Override
- public void run() {
- BluetoothServerSocket serverSocket;
-
- try {
- Log.d(TAG, "getting server socket");
- serverSocket = BluetoothAdapter.getDefaultAdapter()
- .listenUsingInsecureRfcommWithServiceRecord(
- SERVICE_NAME, SERVICE_UUID);
- } catch (IOException e) {
- Log.d(TAG, "error creating server socket, stopping thread");
- bttraffic.stopService();
- return;
- }
-
- Log.d(TAG, "got server socket, starting accept loop");
- BluetoothSocket socket = null;
- try {
- Log.d(TAG, "accepting");
- socket = serverSocket.accept();
-
- if (!Thread.interrupted()) {
- Log.d(TAG, "accepted, listening");
- doListening(socket.getInputStream(), socket.getOutputStream());
- Log.d(TAG, "listen finished");
- }
- } catch (IOException e) {
- Log.d(TAG, "error while accepting or listening", e);
- } finally {
- Log.d(TAG, "Linster interruped");
- Log.d(TAG, "closing socket and stopping service");
- safeClose(serverSocket);
- safeClose(socket);
- if (!bttraffic.isShuttingDown)
- bttraffic.stopService();
- }
-
- }
-
- private void doListening(InputStream inputStream, OutputStream outputStream)
- throws IOException {
- ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize);
-
- while (!Thread.interrupted()) {
- readBytesIntoBuffer(inputStream, byteBuffer, 4);
- byteBuffer.flip();
- int length = byteBuffer.getInt();
- if (Thread.interrupted())
- break;
- readBytesIntoBuffer(inputStream, byteBuffer, length);
-
- if (sendAck)
- outputStream.write(0x55);
- }
- }
-
- void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead)
- throws IOException {
- byteBuffer.clear();
- while (true) {
- int position = byteBuffer.position();
- int remaining = numToRead - position;
- if (remaining == 0) {
- break;
- }
- int count = inputStream.read(byteBuffer.array(), position, remaining);
- if (count < 0) {
- throw new IOException("read the EOF");
- }
- byteBuffer.position(position + count);
- }
- }
- }
-
- public static class SenderRunnable implements Runnable {
- private final BTtraffic bttraffic;
- private final String remoteAddr;
- private final int pkgsize, period;
- private final int defaultpkgsize = 1024;
- private final int defaultperiod = 5000;
- private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
-
- public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) {
- this.bttraffic = bttraffic;
- this.remoteAddr = remoteAddr;
- this.pkgsize = intent.getIntExtra("size", defaultpkgsize);
- this.period = intent.getIntExtra("period", defaultperiod);
- }
-
- @Override
- public void run() {
- BluetoothDevice device = null;
- try {
- device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
- } catch (IllegalArgumentException e) {
- Log.d(TAG, "Invalid BT MAC address!\n");
- }
- if (device == null) {
- Log.d(TAG, "can't find matching device, stopping thread and service");
- bttraffic.stopService();
- return;
- }
-
- BluetoothSocket socket = null;
- try {
- Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr);
- socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID);
- socket.connect();
- Log.d(TAG, "connected, starting to send");
- doSending(socket.getOutputStream());
- Log.d(TAG, "send stopped, stopping service");
- } catch (Exception e) {
- Log.d(TAG, "error while sending", e);
- } finally {
- Log.d(TAG, "finishing, closing thread and service");
- safeClose(socket);
- if (!bttraffic.isShuttingDown)
- bttraffic.stopService();
- }
- }
-
- private void doSending(OutputStream outputStream) throws IOException {
- Log.w(TAG, "doSending");
- try {
- Random random = new Random(System.currentTimeMillis());
-
- byte[] bytes = new byte[pkgsize];
- random.nextBytes(bytes);
- while (!Thread.interrupted()) {
- writeBytes(outputStream, bytes.length);
- outputStream.write(bytes, 0, bytes.length);
- if (period < 0)
- break;
- if (period == 0)
- continue;
-
- SystemClock.sleep(period);
- }
- Log.d(TAG, "Sender interrupted");
- } catch (IOException e) {
- Log.d(TAG, "doSending got error", e);
- }
- }
-
- private static void writeBytes(OutputStream outputStream, int value) throws IOException {
- lengthBuffer.putInt(value);
- lengthBuffer.flip();
- outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit());
- }
- }
-
-}
diff --git a/core/tests/SvcMonitor/Android.bp b/core/tests/SvcMonitor/Android.bp
deleted file mode 100644
index 606e87cb0f4d..000000000000
--- a/core/tests/SvcMonitor/Android.bp
+++ /dev/null
@@ -1,7 +0,0 @@
-android_app {
- name: "svcmonitor",
- srcs: ["src/**/*.java"],
- resource_dirs: ["res"],
- sdk_version: "current",
- certificate: "platform",
-}
diff --git a/core/tests/SvcMonitor/AndroidManifest.xml b/core/tests/SvcMonitor/AndroidManifest.xml
deleted file mode 100644
index de5a9bdaed41..000000000000
--- a/core/tests/SvcMonitor/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.experimental.svcmonitor" >
-
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
-
- <uses-sdk
- android:minSdkVersion="18"
- android:targetSdkVersion="18"
- />
- <application
- android:allowBackup="false"
- android:label="@string/app_name" >
- <service
- android:name=".SvcMonitor"
- android:enabled="true"
- android:exported="true" >
- </service>
- </application>
-
-</manifest>
diff --git a/core/tests/SvcMonitor/README b/core/tests/SvcMonitor/README
deleted file mode 100644
index 13a4380589b4..000000000000
--- a/core/tests/SvcMonitor/README
+++ /dev/null
@@ -1,27 +0,0 @@
-This Android service measures CPU usage of a program and an underlying system service it relies on.
-An example of this would be an android app XYZ communicates to some other device via Bluetooth. The
-SvcMonitor service can monitor the CPU usage of XYZ and com.android.bluetooth.
-
-Usage:
-
-To start the service:
-$ adb shell am startservice -a start \
--e java XYZ -e hal com.android.bluetooth \
-com.google.android.experimental.svcmonitor/.SvcMonitor
-
-To stop the service:
-$ adb shell am startservice -a stop \
-com.google.android.experimental.svcmonitor/.SvcMonitor
-
-To stop the service config:
-$ adb shell am startservice -a change \
--e java NewName -e hal NewService \
-com.google.android.experimental.svcmonitor/.SvcMonitor
-
-To monitor the data:
-$ adb logcat | grep XYZ
-
-Options:
--e java NameOfProgram: any running process’s name.
--e hal NameOfSysService: name of the system service the previous process relies on.
---ei period: period between each measurement (frequency). Unit: ms, Default:1000, Min: 100
diff --git a/core/tests/SvcMonitor/res/values/strings.xml b/core/tests/SvcMonitor/res/values/strings.xml
deleted file mode 100644
index e70276e03647..000000000000
--- a/core/tests/SvcMonitor/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
- <string name="app_name">Bluetooth Test</string>
-</resources>
diff --git a/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java
deleted file mode 100644
index a451445530cd..000000000000
--- a/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java
+++ /dev/null
@@ -1,209 +0,0 @@
-package com.google.android.experimental.svcmonitor;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.lang.Runnable;
-import java.lang.Thread;
-import java.util.Set;
-
-public class SvcMonitor extends Service {
- public static final String TAG = "svcmonitor";
- String javaProc, halProc;
- volatile Thread tMonitor;
- int period;
-
- public SvcMonitor() {};
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent == null) {
- stopSelf();
- return 0;
- }
- Log.d(TAG, "Starting SvcMonitor");
- if ("stop".equals(intent.getAction())) {
- stopService();
- } else if ("start".equals(intent.getAction())) {
- startMonitor(intent);
- } else if ("change".equals(intent.getAction())) {
- changeConfig(intent);
- } else {
- Log.d(TAG, "unknown action: + " + intent.getAction());
- }
- return 0;
- }
-
- private void changeConfig(Intent intent) {
- if (tMonitor == null) {
- Log.d(TAG, "Service not active. Start service first");
- return;
- }
- stopThread();
- startMonitor(intent);
- }
-
- private void startMonitor(Intent intent) {
- if (tMonitor != null) {
- Log.d(TAG, "thread already active");
- return;
- }
- javaProc = intent.getStringExtra("java");
- halProc = intent.getStringExtra("hal");
- period = intent.getIntExtra("period", 1000);
- if (javaProc == null || halProc == null || period < 100) {
- Log.d(TAG, "Failed starting monitor, invalid arguments.");
- stopSelf();
- return;
- }
- Runnable monitor = new MonitorRunnable(this);
- tMonitor = new Thread(monitor);
- tMonitor.start();
- }
-
- private void stopService() {
- stopThread();
- stopSelf();
- Log.d(TAG, "SvcMonitor stopped");
- }
-
- private void stopThread() {
- if (tMonitor == null) {
- Log.d(TAG, "no active thread");
- return;
- }
- Log.d(TAG, "interrupting monitor thread");
- tMonitor.interrupt();
- try {
- tMonitor.join();
- } catch (InterruptedException e) {
- Log.d(TAG, "Unable to finish monitor thread");
- }
- tMonitor = null;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- throw new UnsupportedOperationException("Not yet implemented");
- }
-
- public static class MonitorRunnable implements Runnable {
- long java_time_old, hal_time_old, cpu_time_old = -1;
- String javaPID, halPID;
- SvcMonitor svcmonitor;
- static String javaProcTAG;
- int period;
-
- public MonitorRunnable(SvcMonitor svcmonitor) {
- this.svcmonitor = svcmonitor;
- this.period = svcmonitor.period;
- javaPID = getPIDof(svcmonitor.javaProc);
- halPID = getPIDof(svcmonitor.halProc);
- java_time_old = getPsTime(javaPID);
- hal_time_old = getPsTime(halPID);
- cpu_time_old = getPsTime("");
- javaProcTAG = String.valueOf(svcmonitor.javaProc.toCharArray());
- }
-
- @Override
- public void run() {
- if (halPID.isEmpty() || javaPID.isEmpty()) {
- Log.d(javaProcTAG, "No such process: " +
- (halPID.isEmpty() ? svcmonitor.halProc : svcmonitor.javaProc));
- return;
- }
- while (!Thread.interrupted()) {
- calculateUsage();
- SystemClock.sleep(period);
- }
- Log.d(TAG, "Stopping monitor thread");
- }
-
- private void calculateUsage() {
- long java_time = getPsTime(javaPID);
- long hal_time = getPsTime(halPID);
- long cpu_time = getPsTime("");
-
- if (cpu_time_old >= 0) {
- float java_diff = (float) (java_time - java_time_old);
- float hal_diff = (float) (hal_time - hal_time_old);
- float cpu_diff = (float) (cpu_time - cpu_time_old);
- Log.w(javaProcTAG, "\n----------------\n");
- Log.w(javaProcTAG, "JAVA level CPU: "
- + (java_diff * 100.0 / cpu_diff) + "%\n");
- Log.w(javaProcTAG, " HAL level CPU: "
- + (hal_diff * 100.0 / cpu_diff) + "%\n");
- Log.w(javaProcTAG, " SYS level CPU: "
- + ((java_diff + hal_diff) * 100.0 / cpu_diff) + "%\n");
- } else {
- Log.w(TAG, "Waiting for status\n");
- }
-
- java_time_old = java_time;
- hal_time_old = hal_time;
- cpu_time_old = cpu_time;
- }
-
- private String getPIDof(String psName) {
- String pid = "";
-
- try {
- String[] cmd = {"/system/bin/sh", "-c", "ps | grep " + psName};
- Process ps = Runtime.getRuntime().exec(cmd);
- BufferedReader in = new BufferedReader(
- new InputStreamReader(ps.getInputStream()));
- String temp = in.readLine();
- if (temp == null || temp.isEmpty())
- throw new IOException("No such process: " + psName);
- pid = temp.split(" +")[1];
- in.close();
- } catch (IOException e) {
- Log.d(javaProcTAG, "Error finding PID of process: " + psName + "\n", e);
- }
- return pid;
- }
-
- private long getPsTime(String pid) {
- String psStat = getPsStat("/" + pid);
- String[] statBreakDown = psStat.split(" +");
- long psTime;
-
- if (pid.isEmpty()) {
- psTime = Long.parseLong(statBreakDown[1])
- + Long.parseLong(statBreakDown[2])
- + Long.parseLong(statBreakDown[3])
- + Long.parseLong(statBreakDown[4]);
- } else {
- psTime = Long.parseLong(statBreakDown[13])
- + Long.parseLong(statBreakDown[14]);
- }
-
- return psTime;
- }
-
- private String getPsStat(String psname) {
- String stat = "";
- try {
- FileInputStream fs = new FileInputStream("/proc" + psname + "/stat");
- BufferedReader br = new BufferedReader(new InputStreamReader(fs));
- stat = br.readLine();
- fs.close();
- } catch (IOException e) {
- Log.d(TAG, "Error retreiving stat. \n");
- }
- return stat;
- }
- }
-}
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index f48e66681cc7..b517428f5b59 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -261,4 +261,12 @@ public class ContentResolverTest {
// Expected
}
}
+
+ @Test
+ public void testUncanonicalize() {
+ Uri uncanonical = mResolver.uncanonicalize(
+ Uri.parse("content://android.content.FakeProviderRemote/something"));
+ assertThat(uncanonical).isEqualTo(
+ Uri.parse("content://android.content.FakeProviderRemote/uncanonical"));
+ }
}
diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java
index 2c92da34d9a4..d0bb78101ee4 100644
--- a/core/tests/coretests/src/android/content/FakeProviderRemote.java
+++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java
@@ -66,4 +66,13 @@ public class FakeProviderRemote extends ContentProvider {
return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority())
.appendPath("canonical").build();
}
+
+ @Override
+ public Uri uncanonicalize(Uri uri) {
+ if (uri.getPath() != null && uri.getPath().contains("error")) {
+ throw new IllegalArgumentException("Expected exception");
+ }
+ return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority())
+ .appendPath("uncanonical").build();
+ }
}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 54e8a0c1638e..bda84dd76cf5 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -528,7 +528,7 @@
</font>
<font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
- </family>
+ </family>
<family lang="und-Khmr" variant="compact">
<font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
@@ -762,18 +762,8 @@
<font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
</family>
<family lang="und-Tibt">
- <font weight="400" style="normal">NotoSerifTibetan-VF.ttf
- <axis tag="wght" stylevalue="400" />
- </font>
- <font weight="500" style="normal">NotoSerifTibetan-VF.ttf
- <axis tag="wght" stylevalue="500" />
- </font>
- <font weight="600" style="normal">NotoSerifTibetan-VF.ttf
- <axis tag="wght" stylevalue="600" />
- </font>
- <font weight="700" style="normal">NotoSerifTibetan-VF.ttf
- <axis tag="wght" stylevalue="700" />
- </font>
+ <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansTibetan-Bold.ttf</font>
</family>
<family lang="und-Tfng">
<font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
@@ -903,83 +893,4 @@
<family lang="und-Wara">
<font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font>
</family>
- <family lang="und-Gran">
- <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font>
- </family>
- <family lang="und-Modi">
- <font weight="400" style="normal">NotoSansModi-Regular.ttf</font>
- </family>
- <family lang="und-Dogr">
- <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
- </family>
- <family lang="und-Medf">
- <font weight="400" style="normal">NotoSansMedefaidrin-VF.ttf
- <axis tag="wght" stylevalue="400" />
- </font>
- <font weight="500" style="normal">NotoSansMedefaidrin-VF.ttf
- <axis tag="wght" stylevalue="500" />
- </font>
- <font weight="600" style="normal">NotoSansMedefaidrin-VF.ttf
- <axis tag="wght" stylevalue="600" />
- </font>
- <font weight="700" style="normal">NotoSansMedefaidrin-VF.ttf
- <axis tag="wght" stylevalue="700" />
- </font>
- </family>
- <family lang="und-Soyo">
- <font weight="400" style="normal">NotoSansSoyombo-VF.ttf
- <axis tag="wght" stylevalue="400" />
- </font>
- <font weight="500" style="normal">NotoSansSoyombo-VF.ttf
- <axis tag="wght" stylevalue="500" />
- </font>
- <font weight="600" style="normal">NotoSansSoyombo-VF.ttf
- <axis tag="wght" stylevalue="600" />
- </font>
- <font weight="700" style="normal">NotoSansSoyombo-VF.ttf
- <axis tag="wght" stylevalue="700" />
- </font>
- </family>
- <family lang="und-Takr">
- <font weight="400" style="normal">NotoSansTakri-VF.ttf
- <axis tag="wght" stylevalue="400" />
- </font>
- <font weight="500" style="normal">NotoSansTakri-VF.ttf
- <axis tag="wght" stylevalue="500" />
- </font>
- <font weight="600" style="normal">NotoSansTakri-VF.ttf
- <axis tag="wght" stylevalue="600" />
- </font>
- <font weight="700" style="normal">NotoSansTakri-VF.ttf
- <axis tag="wght" stylevalue="700" />
- </font>
- </family>
- <family lang="und-Hmnp">
- <font weight="400" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
- <axis tag="wght" stylevalue="400" />
- </font>
- <font weight="500" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
- <axis tag="wght" stylevalue="500" />
- </font>
- <font weight="600" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
- <axis tag="wght" stylevalue="600" />
- </font>
- <font weight="700" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
- <axis tag="wght" stylevalue="700" />
- </font>
- </family>
- <family lang="und-Yezi">
- <font weight="400" style="normal">NotoSerifYezidi-VF.ttf
- <axis tag="wght" stylevalue="400" />
- </font>
- <font weight="500" style="normal">NotoSerifYezidi-VF.ttf
- <axis tag="wght" stylevalue="500" />
- </font>
- <font weight="600" style="normal">NotoSerifYezidi-VF.ttf
- <axis tag="wght" stylevalue="600" />
- </font>
- <font weight="700" style="normal">NotoSerifYezidi-VF.ttf
- <axis tag="wght" stylevalue="700" />
- </font>
- </family>
</familyset>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml
index d2f235e273d5..9157f63ce1b3 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml
@@ -16,7 +16,6 @@
-->
<!-- Layout for {@link com.android.wm.shell.pip.tv.PipControlsView}. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
<com.android.wm.shell.pip.tv.PipControlButtonView
android:id="@+id/full_button"
android:layout_width="@dimen/picture_in_picture_button_width"
@@ -31,13 +30,4 @@
android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
android:src="@drawable/pip_ic_close_white"
android:text="@string/pip_close" />
-
- <com.android.wm.shell.pip.tv.PipControlButtonView
- android:id="@+id/play_pause_button"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
- android:src="@drawable/pip_ic_pause_white"
- android:text="@string/pip_pause"
- android:visibility="gone" />
</merge>
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 17418f934691..bd6c1e096d01 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
@@ -30,6 +30,7 @@ import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
+import android.window.DisplayAreaAppearedInfo;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
import android.window.WindowContainerTransaction;
@@ -189,6 +190,17 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
}
@Override
+ public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
+ final List<DisplayAreaAppearedInfo> displayAreaInfos =
+ super.registerOrganizer(displayAreaFeature);
+ for (int i = 0; i < displayAreaInfos.size(); i++) {
+ final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
+ onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
+ }
+ return displayAreaInfos;
+ }
+
+ @Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
mUpdateHandler.post(() -> resetWindowsOffset(null));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 59c79bafd46c..8d5da1a5ffcb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -24,7 +24,6 @@ import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.pip.tv.PipController;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -34,12 +33,6 @@ import java.util.function.Consumer;
*/
public interface Pip {
/**
- * Registers a {@link PipController.MediaListener} to PipController.
- */
- default void addMediaListener(PipController.MediaListener listener) {
- }
-
- /**
* Closes PIP (PIPed activity and PIP system UI).
*/
default void closePip() {
@@ -145,12 +138,6 @@ public interface Pip {
}
/**
- * Removes a {@link PipController.MediaListener} from PipController.
- */
- default void removeMediaListener(PipController.MediaListener listener) {
- }
-
- /**
* Resize the Pip to the appropriate size for the input state.
*
* @param state In Pip state also used to determine the new size for the Pip.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 3a675c49e712..d0ab31dd72c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -42,7 +42,10 @@ public final class PipBoundsState {
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final DisplayLayout mDisplayLayout = new DisplayLayout();
- void setBounds(@NonNull Rect bounds) {
+ /**
+ * Set the current PIP bounds.
+ */
+ public void setBounds(@NonNull Rect bounds) {
mBounds.set(bounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index d506ca8d6470..37a5919b1c9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -29,7 +29,6 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
-import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -45,6 +44,7 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
@@ -62,7 +62,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private static final String TAG = "PipController";
private Context mContext;
- private Handler mHandler = new Handler();
+ private ShellExecutor mMainExecutor;
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
@@ -82,6 +82,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
protected PipMenuActivityController mMenuController;
protected PipTaskOrganizer mPipTaskOrganizer;
+ protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener =
+ new PipControllerPinnedStackListener();
/**
* Handler for display rotation changes.
@@ -150,12 +152,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onListenerRegistered(IPinnedStackController controller) {
- mHandler.post(() -> mTouchHandler.setPinnedStackController(controller));
+ mMainExecutor.execute(() -> mTouchHandler.setPinnedStackController(controller));
}
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight);
mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
});
@@ -163,19 +165,19 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void onMovementBoundsChanged(boolean fromImeAdjustment) {
- mHandler.post(() -> updateMovementBounds(null /* toBounds */,
+ mMainExecutor.execute(() -> updateMovementBounds(null /* toBounds */,
false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */,
null /* windowContainerTransaction */));
}
@Override
public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
- mHandler.post(() -> mMenuController.setAppActions(actions));
+ mMainExecutor.execute(() -> mMenuController.setAppActions(actions));
}
@Override
public void onActivityHidden(ComponentName componentName) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
if (componentName.equals(mPipBoundsState.getLastPipComponentName())) {
// The activity was removed, we don't want to restore to the reentry state
// saved for this component anymore.
@@ -186,12 +188,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void onDisplayInfoChanged(DisplayInfo displayInfo) {
- mHandler.post(() -> mPipBoundsState.setDisplayInfo(displayInfo));
+ mMainExecutor.execute(() -> mPipBoundsState.setDisplayInfo(displayInfo));
}
@Override
public void onConfigurationChanged() {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mPipBoundsHandler.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
});
@@ -201,7 +203,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
public void onAspectRatioChanged(float aspectRatio) {
// TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params
// change.
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mPipBoundsState.setAspectRatio(aspectRatio);
mTouchHandler.onAspectRatioChanged();
});
@@ -217,7 +219,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PipMenuActivityController pipMenuActivityController,
PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler,
- WindowManagerShellWrapper windowManagerShellWrapper
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ ShellExecutor mainExecutor
) {
// Ensure that we are the primary user's SystemUI.
final int processUser = UserManager.get(context).getUserHandle();
@@ -231,6 +234,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mPipBoundsHandler = pipBoundsHandler;
mPipBoundsState = pipBoundsState;
mPipTaskOrganizer = pipTaskOrganizer;
+ mMainExecutor = mainExecutor;
mPipTaskOrganizer.registerPipTransitionCallback(this);
mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
final DisplayInfo newDisplayInfo = new DisplayInfo();
@@ -254,8 +258,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mPipBoundsState.setDisplayInfo(displayInfo);
try {
- mWindowManagerShellWrapper.addPinnedStackListener(
- new PipControllerPinnedStackListener());
+ mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register pinned stack listener", e);
}
@@ -263,14 +266,14 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void onDensityOrFontScaleChanged() {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext);
});
}
@Override
public void onActivityPinned(String packageName) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mTouchHandler.onActivityPinned();
mMediaController.onActivityPinned();
mMenuController.onActivityPinned();
@@ -280,7 +283,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void onActivityUnpinned(ComponentName topActivity) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mMenuController.onActivityUnpinned();
mTouchHandler.onActivityUnpinned(topActivity);
mAppOpsListener.onActivityUnpinned();
@@ -299,7 +302,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void onOverlayChanged() {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
updateMovementBounds(null /* toBounds */,
false /* fromRotation */, false /* fromImeAdjustment */,
@@ -358,7 +361,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
*/
@Override
public void setShelfHeight(boolean visible, int height) {
- mHandler.post(() -> setShelfHeightLocked(visible, height));
+ mMainExecutor.execute(() -> setShelfHeightLocked(visible, height));
}
private void setShelfHeightLocked(boolean visible, int height) {
@@ -374,12 +377,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void setPinnedStackAnimationType(int animationType) {
- mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType));
+ mMainExecutor.execute(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType));
}
@Override
public void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
- mHandler.post(() -> mPinnedStackAnimationRecentsCallback = callback);
+ mMainExecutor.execute(() -> mPinnedStackAnimationRecentsCallback = callback);
}
@Override
@@ -476,7 +479,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
PipMenuActivityController pipMenuActivityController,
PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler,
- WindowManagerShellWrapper windowManagerShellWrapper) {
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
Slog.w(TAG, "Device doesn't support Pip feature");
return null;
@@ -484,6 +488,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
return new PipController(context, displayController, pipAppOpsListener, pipBoundsHandler,
pipBoundsState, pipMediaController, pipMenuActivityController,
- pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper);
+ pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper, mainExecutor);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index b5fa03082401..9240b3f41ff4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -67,16 +67,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private final Context mContext;
private final PipTaskOrganizer mPipTaskOrganizer;
- private final @NonNull PipBoundsState mPipBoundsState;
+ private @NonNull PipBoundsState mPipBoundsState;
private PipMenuActivityController mMenuController;
private PipSnapAlgorithm mSnapAlgorithm;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
- /** PIP's current bounds on the screen. */
- private final Rect mBounds = new Rect();
-
/** The bounds within which PIP's top-left coordinate is allowed to move. */
private final Rect mMovementBounds = new Rect();
@@ -140,7 +137,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
mMainHandler.post(() -> {
mMenuController.updateMenuLayout(newBounds);
- mBounds.set(newBounds);
+ mPipBoundsState.setBounds(newBounds);
});
};
@@ -196,7 +193,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mResizePipUpdateListener = (target, values) -> {
if (!mTemporaryBounds.isEmpty()) {
mPipTaskOrganizer.scheduleUserResizePip(
- mBounds, mTemporaryBounds, null);
+ getBounds(), mTemporaryBounds, null);
}
};
}
@@ -204,7 +201,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
@NonNull
@Override
public Rect getFloatingBoundsOnScreen() {
- return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : mBounds;
+ return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : getBounds();
}
@NonNull
@@ -223,7 +220,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
*/
void synchronizePinnedStackBounds() {
cancelAnimations();
- mBounds.set(mPipBoundsState.getBounds());
mTemporaryBounds.setEmpty();
if (mPipTaskOrganizer.isInPip()) {
@@ -261,10 +257,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
if (!isDragging) {
resizePipUnchecked(toBounds);
- mBounds.set(toBounds);
+ mPipBoundsState.setBounds(toBounds);
} else {
mTemporaryBounds.set(toBounds);
- mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds,
+ mPipTaskOrganizer.scheduleUserResizePip(getBounds(), mTemporaryBounds,
(Rect newBounds) -> {
mMainHandler.post(() -> {
mMenuController.updateMenuLayout(newBounds);
@@ -275,8 +271,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
// If PIP is 'catching up' after being stuck in the dismiss target, update the animation
// to spring towards the new touch location.
mTemporaryBoundsPhysicsAnimator
- .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig)
- .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig)
+ .spring(FloatProperties.RECT_WIDTH, getBounds().width(), mSpringConfig)
+ .spring(FloatProperties.RECT_HEIGHT, getBounds().height(), mSpringConfig)
.spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
.spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
@@ -292,8 +288,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
boolean flung, Function0<Unit> after) {
final PointF targetCenter = target.getCenterOnScreen();
- final float desiredWidth = mBounds.width() / 2;
- final float desiredHeight = mBounds.height() / 2;
+ final float desiredWidth = getBounds().width() / 2;
+ final float desiredHeight = getBounds().height() / 2;
final float destinationX = targetCenter.x - (desiredWidth / 2f);
final float destinationY = targetCenter.y - (desiredHeight / 2f);
@@ -301,7 +297,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
// If we're already in the dismiss target area, then there won't be a move to set the
// temporary bounds, so just initialize it to the current bounds
if (mTemporaryBounds.isEmpty()) {
- mTemporaryBounds.set(mBounds);
+ mTemporaryBounds.set(getBounds());
}
mTemporaryBoundsPhysicsAnimator
.spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig)
@@ -365,15 +361,17 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
// The movement bounds represent the area within which we can move PIP's top-left position.
// The allowed area for all of PIP is those bounds plus PIP's width and height.
mFloatingAllowedArea.set(mMovementBounds);
- mFloatingAllowedArea.right += mBounds.width();
- mFloatingAllowedArea.bottom += mBounds.height();
+ mFloatingAllowedArea.right += getBounds().width();
+ mFloatingAllowedArea.bottom += getBounds().height();
}
/**
* @return the PiP bounds.
+ *
+ * TODO(b/169373982): can be private, outside callers can use PipBoundsState directly.
*/
Rect getBounds() {
- return mBounds;
+ return mPipBoundsState.getBounds();
}
/**
@@ -381,7 +379,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* otherwise.
*/
Rect getPossiblyAnimatingBounds() {
- return mTemporaryBounds.isEmpty() ? mBounds : mTemporaryBounds;
+ return mTemporaryBounds.isEmpty() ? getBounds() : mTemporaryBounds;
}
/**
@@ -407,8 +405,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mSpringingToTouch = false;
mTemporaryBoundsPhysicsAnimator
- .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig)
- .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig)
+ .spring(FloatProperties.RECT_WIDTH, getBounds().width(), mSpringConfig)
+ .spring(FloatProperties.RECT_HEIGHT, getBounds().height(), mSpringConfig)
.flingThenSpring(
FloatProperties.RECT_X, velocityX, isStash ? mStashConfigX : mFlingConfigX,
mSpringConfig, true /* flingMustReachMinOrMax */)
@@ -416,7 +414,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
.withEndActions(endAction);
- final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO);
+ final float offset = ((float) getBounds().width()) * (1.0f - STASH_RATIO);
final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left;
final float rightEdge = isStash ? mMovementBounds.right + offset : mMovementBounds.right;
@@ -436,7 +434,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
// Animate from the current bounds if we're not already animating.
- mTemporaryBounds.set(mBounds);
+ mTemporaryBounds.set(getBounds());
}
mTemporaryBoundsPhysicsAnimator
@@ -453,13 +451,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
// Animate off the bottom of the screen, then dismiss PIP.
mTemporaryBoundsPhysicsAnimator
.spring(FloatProperties.RECT_Y,
- mMovementBounds.bottom + mBounds.height() * 2,
+ mMovementBounds.bottom + getBounds().height() * 2,
0,
mSpringConfig)
.withEndActions(this::dismissPip);
startBoundsAnimator(
- mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */,
+ getBounds().left /* toX */, getBounds().bottom + getBounds().height() /* toY */,
true /* dismiss */);
mDismissalPending = false;
@@ -470,7 +468,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
*/
float animateToExpandedState(Rect expandedBounds, Rect movementBounds,
Rect expandedMovementBounds, Runnable callback) {
- float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), movementBounds);
+ float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(getBounds()),
+ movementBounds);
mSnapAlgorithm.applySnapFraction(expandedBounds, expandedMovementBounds, savedSnapFraction);
mPostPipTransitionCallback = callback;
resizeAndAnimatePipUnchecked(expandedBounds, EXPAND_STACK_TO_MENU_DURATION);
@@ -484,7 +483,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) {
if (savedSnapFraction < 0f) {
// If there are no saved snap fractions, then just use the current bounds
- savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
+ savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(getBounds()),
currentMovementBounds);
}
mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
@@ -525,7 +524,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right);
mFlingConfigY = new PhysicsAnimator.FlingConfig(
DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
- final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO);
+ final float offset = ((float) getBounds().width()) * (1.0f - STASH_RATIO);
mStashConfigX = new PhysicsAnimator.FlingConfig(
DEFAULT_FRICTION, mMovementBounds.left - offset, mMovementBounds.right + offset);
}
@@ -547,8 +546,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mAnimatingToBounds.set(
(int) toX,
(int) toY,
- (int) toX + mBounds.width(),
- (int) toY + mBounds.height());
+ (int) toX + getBounds().width(),
+ (int) toY + getBounds().height());
setAnimatingToBounds(mAnimatingToBounds);
if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
@@ -572,11 +571,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
if (!mDismissalPending
&& !mSpringingToTouch
&& !mMagnetizedPip.getObjectStuckToTarget()) {
- mBounds.set(mTemporaryBounds);
+ mPipBoundsState.setBounds(mTemporaryBounds);
if (!mDismissalPending) {
// do not schedule resize if PiP is dismissing, which may cause app re-open to
// mBounds instead of it's normal bounds.
- mPipTaskOrganizer.scheduleFinishResizePip(mBounds);
+ mPipTaskOrganizer.scheduleFinishResizePip(getBounds());
}
mTemporaryBounds.setEmpty();
}
@@ -604,7 +603,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
Log.d(TAG, "resizePipUnchecked: toBounds=" + toBounds
+ " callers=\n" + Debug.getCallers(5, " "));
}
- if (!toBounds.equals(mBounds)) {
+ if (!toBounds.equals(getBounds())) {
mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback);
}
}
@@ -657,6 +656,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mBounds=" + mBounds);
+ pw.println(innerPrefix + "mBounds=" + getBounds());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 8e9bf7434b3c..3468b888c06a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -38,9 +38,6 @@ import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
-import android.media.session.MediaController;
-import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
import android.os.Debug;
import android.os.Handler;
import android.os.RemoteException;
@@ -55,6 +52,7 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipTaskOrganizer;
import java.util.ArrayList;
@@ -110,22 +108,19 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private final PipBoundsState mPipBoundsState;
private final PipBoundsHandler mPipBoundsHandler;
private final PipTaskOrganizer mPipTaskOrganizer;
+ private final PipMediaController mPipMediaController;
private IActivityTaskManager mActivityTaskManager;
- private MediaSessionManager mMediaSessionManager;
private int mState = STATE_NO_PIP;
private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP;
private final Handler mHandler = new Handler();
private List<Listener> mListeners = new ArrayList<>();
- private List<MediaListener> mMediaListeners = new ArrayList<>();
private Rect mPipBounds;
private Rect mDefaultPipBounds = new Rect();
private Rect mMenuModePipBounds;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private int mPipTaskId = TASK_ID_NO_PIP;
private int mPinnedStackId = INVALID_STACK_ID;
- private ComponentName mPipComponentName;
- private MediaController mPipMediaController;
private String[] mLastPackagesResourceGranted;
private PipNotification mPipNotification;
private ParceledListSlice<RemoteAction> mCustomActions;
@@ -168,17 +163,13 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
}
};
- private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
- controllers -> updateMediaController(controllers);
+
private final PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener =
new PipControllerPinnedStackListener();
@Override
public void registerSessionListenerForCurrentUser() {
- // TODO Need confirm if TV have to re-registers when switch user
- mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
- mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveMediaSessionListener, null,
- UserHandle.USER_CURRENT, null);
+ mPipMediaController.registerSessionListenerForCurrentUser();
}
/**
@@ -232,12 +223,14 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PipBoundsState pipBoundsState,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
- WindowManagerShellWrapper windowManagerShellWrapper
- ) {
+ PipMediaController pipMediaController,
+ PipNotification pipNotification,
+ WindowManagerShellWrapper windowManagerShellWrapper) {
mContext = context;
mPipBoundsState = pipBoundsState;
- mPipNotification = new PipNotification(context, this);
+ mPipNotification = pipNotification;
mPipBoundsHandler = pipBoundsHandler;
+ mPipMediaController = pipMediaController;
// Ensure that we have the display info in case we get calls to update the bounds
// before the listener calls back
final DisplayInfo displayInfo = new DisplayInfo();
@@ -250,6 +243,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mPipTaskOrganizer.registerPipTransitionCallback(this);
mActivityTaskManager = ActivityTaskManager.getService();
+ addListener(mPipNotification);
+
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_CLOSE);
intentFilter.addAction(ACTION_MENU);
@@ -261,7 +256,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mLastOrientation = initialConfig.orientation;
loadConfigurationsAndApply(initialConfig);
- mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
mWindowManagerShellWrapper = windowManagerShellWrapper;
try {
mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
@@ -329,8 +323,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mState = STATE_NO_PIP;
mPipTaskId = TASK_ID_NO_PIP;
- mPipMediaController = null;
- mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
if (removePipStack) {
try {
mActivityTaskManager.removeTask(mPinnedStackId);
@@ -371,13 +363,9 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo);
mPinnedStackId = taskInfo.taskId;
mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1];
- mPipComponentName = ComponentName.unflattenFromString(
- taskInfo.childTaskNames[taskInfo.childTaskNames.length - 1]);
// Set state to STATE_PIP so we show it when the pinned stack animation ends.
mState = STATE_PIP;
- mMediaSessionManager.addOnActiveSessionsChangedListener(
- mActiveMediaSessionListener, null);
- updateMediaController(mMediaSessionManager.getActiveSessions(null));
+ mPipMediaController.onActivityPinned();
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onPipEntered(packageName);
}
@@ -554,20 +542,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
/**
- * Adds a {@link MediaListener} to PipController.
- */
- public void addMediaListener(MediaListener listener) {
- mMediaListeners.add(listener);
- }
-
- /**
- * Removes a {@link MediaListener} from PipController.
- */
- public void removeMediaListener(MediaListener listener) {
- mMediaListeners.remove(listener);
- }
-
- /**
* Returns {@code true} if PIP is shown.
*/
public boolean isPipShown() {
@@ -608,69 +582,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
}
- private void updateMediaController(List<MediaController> controllers) {
- MediaController mediaController = null;
- if (controllers != null && getState() != STATE_NO_PIP && mPipComponentName != null) {
- for (int i = controllers.size() - 1; i >= 0; i--) {
- MediaController controller = controllers.get(i);
- // We assumes that an app with PIPable activity
- // keeps the single instance of media controller especially when PIP is on.
- if (controller.getPackageName().equals(mPipComponentName.getPackageName())) {
- mediaController = controller;
- break;
- }
- }
- }
- if (mPipMediaController != mediaController) {
- mPipMediaController = mediaController;
- for (int i = mMediaListeners.size() - 1; i >= 0; i--) {
- mMediaListeners.get(i).onMediaControllerChanged();
- }
- if (mPipMediaController == null) {
- mHandler.postDelayed(mClosePipRunnable,
- CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS);
- } else {
- mHandler.removeCallbacks(mClosePipRunnable);
- }
- }
- }
-
- /**
- * Gets the {@link android.media.session.MediaController} for the PIPed activity.
- */
- MediaController getMediaController() {
- return mPipMediaController;
- }
-
@Override
public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
-
}
- /**
- * Returns the PIPed activity's playback state.
- * This returns one of {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
- * or {@link #PLAYBACK_STATE_UNAVAILABLE}.
- */
- int getPlaybackState() {
- if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
- return PLAYBACK_STATE_UNAVAILABLE;
- }
- int state = mPipMediaController.getPlaybackState().getState();
- boolean isPlaying = (state == PlaybackState.STATE_BUFFERING
- || state == PlaybackState.STATE_CONNECTING
- || state == PlaybackState.STATE_PLAYING
- || state == PlaybackState.STATE_FAST_FORWARDING
- || state == PlaybackState.STATE_REWINDING
- || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
- || state == PlaybackState.STATE_SKIPPING_TO_NEXT);
- long actions = mPipMediaController.getPlaybackState().getActions();
- if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
- return PLAYBACK_STATE_PAUSED;
- } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
- return PLAYBACK_STATE_PLAYING;
- }
- return PLAYBACK_STATE_UNAVAILABLE;
+ PipMediaController getPipMediaController() {
+ return mPipMediaController;
}
@Override
@@ -718,14 +635,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
void onPipResizeAboutToStart();
}
- /**
- * A listener interface to receive change in PIP's media controller
- */
- public interface MediaListener {
- /** Invoked when the MediaController on PIPed activity is changed. */
- void onMediaControllerChanged();
- }
-
private String getStateDescription() {
if (mSuspendPipResizingReason == 0) {
return stateToName(mState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java
index 14960c38fd43..95d9b77c513e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java
@@ -51,15 +51,11 @@ public class PipControlsView extends LinearLayout {
setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
}
- PipControlButtonView getFullButtonView() {
+ PipControlButtonView getFullscreenButton() {
return findViewById(R.id.full_button);
}
- PipControlButtonView getCloseButtonView() {
+ PipControlButtonView getCloseButton() {
return findViewById(R.id.close_button);
}
-
- PipControlButtonView getPlayPauseButtonView() {
- return findViewById(R.id.play_pause_button);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java
index f66e9025a9ed..5265e7705ed9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java
@@ -18,10 +18,10 @@ package com.android.wm.shell.pip.tv;
import android.app.PendingIntent;
import android.app.RemoteAction;
+import android.content.Context;
import android.graphics.Color;
-import android.media.session.MediaController;
-import android.media.session.PlaybackState;
import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -29,9 +29,8 @@ import android.view.View;
import com.android.wm.shell.R;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
/**
@@ -42,213 +41,118 @@ public class PipControlsViewController {
private static final float DISABLED_ACTION_ALPHA = 0.54f;
- private final PipControlsView mView;
- private final LayoutInflater mLayoutInflater;
- private final Handler mHandler;
private final PipController mPipController;
- private final PipControlButtonView mPlayPauseButtonView;
- private MediaController mMediaController;
- private PipControlButtonView mFocusedChild;
- private Listener mListener;
- private ArrayList<PipControlButtonView> mCustomButtonViews = new ArrayList<>();
- private List<RemoteAction> mCustomActions = new ArrayList<>();
-
- public PipControlsView getView() {
- return mView;
- }
-
- /**
- * An interface to listen user action.
- */
- public interface Listener {
- /**
- * Called when a user clicks close PIP button.
- */
- void onClosed();
- }
- private View.OnAttachStateChangeListener
- mOnAttachStateChangeListener =
- new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- updateMediaController();
- mPipController.addMediaListener(mPipMediaListener);
- }
+ private final Context mContext;
+ private final Handler mUiThreadHandler;
+ private final PipControlsView mView;
+ private final List<PipControlButtonView> mAdditionalButtons = new ArrayList<>();
- @Override
- public void onViewDetachedFromWindow(View v) {
- mPipController.removeMediaListener(mPipMediaListener);
- }
- };
+ private final List<RemoteAction> mCustomActions = new ArrayList<>();
+ private final List<RemoteAction> mMediaActions = new ArrayList<>();
- private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- updateUserActions();
- }
- };
-
- private final PipController.MediaListener mPipMediaListener = this::updateMediaController;
-
- private final View.OnFocusChangeListener
- mFocusChangeListener =
- new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- if (hasFocus) {
- mFocusedChild = (PipControlButtonView) view;
- } else if (mFocusedChild == view) {
- mFocusedChild = null;
- }
- }
- };
-
- public PipControlsViewController(PipControlsView view, PipController pipController,
- LayoutInflater layoutInflater, Handler handler) {
- super();
- mView = view;
+ public PipControlsViewController(PipControlsView view, PipController pipController) {
+ mContext = view.getContext();
+ mUiThreadHandler = new Handler(Looper.getMainLooper());
mPipController = pipController;
- mLayoutInflater = layoutInflater;
- mHandler = handler;
-
- mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
- if (mView.isAttachedToWindow()) {
- mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
- }
-
- View fullButtonView = mView.getFullButtonView();
- fullButtonView.setOnFocusChangeListener(mFocusChangeListener);
- fullButtonView.setOnClickListener(mView -> mPipController.movePipToFullscreen());
-
- View closeButtonView = mView.getCloseButtonView();
- closeButtonView.setOnFocusChangeListener(mFocusChangeListener);
- closeButtonView.setOnClickListener(v -> {
- mPipController.closePip();
- if (mListener != null) {
- mListener.onClosed();
- }
- });
+ mView = view;
- mPlayPauseButtonView = mView.getPlayPauseButtonView();
- mPlayPauseButtonView.setOnFocusChangeListener(mFocusChangeListener);
- mPlayPauseButtonView.setOnClickListener(v -> {
- if (mMediaController == null || mMediaController.getPlaybackState() == null) {
- return;
- }
- final int playbackState = mPipController.getPlaybackState();
- if (playbackState == PipController.PLAYBACK_STATE_PAUSED) {
- mMediaController.getTransportControls().play();
- } else if (playbackState == PipController.PLAYBACK_STATE_PLAYING) {
- mMediaController.getTransportControls().pause();
- }
+ mView.getFullscreenButton().setOnClickListener(v -> mPipController.movePipToFullscreen());
+ mView.getCloseButton().setOnClickListener(v -> mPipController.closePip());
- // View will be updated later in {@link mMediaControllerCallback}
- });
+ mPipController.getPipMediaController().addActionListener(this::onMediaActionsChanged);
}
- private void updateMediaController() {
- AtomicReference<MediaController> newController = new AtomicReference<>();
- newController.set(mPipController.getMediaController());
+ PipControlsView getView() {
+ return mView;
+ }
- if (newController.get() == null || mMediaController == newController.get()) {
+ /**
+ * Updates the set of activity-defined actions.
+ */
+ void setCustomActions(List<? extends RemoteAction> actions) {
+ if (mCustomActions.isEmpty() && actions.isEmpty()) {
+ // Nothing changed - return early.
return;
}
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaControllerCallback);
+ mCustomActions.clear();
+ mCustomActions.addAll(actions);
+ updateAdditionalActions();
+ }
+
+ private void onMediaActionsChanged(List<RemoteAction> actions) {
+ if (mMediaActions.isEmpty() && actions.isEmpty()) {
+ // Nothing changed - return early.
+ return;
}
- mMediaController = newController.get();
- if (mMediaController != null) {
- mMediaController.registerCallback(mMediaControllerCallback);
+ mMediaActions.clear();
+ mMediaActions.addAll(actions);
+
+ // Update the view only if there are no custom actions (media actions are only shown when
+ // there no custom actions).
+ if (mCustomActions.isEmpty()) {
+ updateAdditionalActions();
}
- updateUserActions();
}
- /**
- * Updates the actions for the PIP. If there are no custom actions, then the media session
- * actions are shown.
- */
- private void updateUserActions() {
+ private void updateAdditionalActions() {
+ final List<RemoteAction> actionsToDisplay;
if (!mCustomActions.isEmpty()) {
- // Ensure we have as many buttons as actions
- while (mCustomButtonViews.size() < mCustomActions.size()) {
- PipControlButtonView buttonView = (PipControlButtonView) mLayoutInflater.inflate(
+ // If there are custom actions: show them.
+ actionsToDisplay = mCustomActions;
+ } else if (!mMediaActions.isEmpty()) {
+ // If there are no custom actions, but there media actions: show them.
+ actionsToDisplay = mMediaActions;
+ } else {
+ // If there no custom actions and no media actions: clean up all the additional buttons.
+ actionsToDisplay = Collections.emptyList();
+ }
+
+ // Make sure we exactly as many additional buttons as we have actions to display.
+ final int actionsNumber = actionsToDisplay.size();
+ int buttonsNumber = mAdditionalButtons.size();
+ if (actionsNumber > buttonsNumber) {
+ final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ // Add buttons until we have enough to display all of the actions.
+ while (actionsNumber > buttonsNumber) {
+ final PipControlButtonView button = (PipControlButtonView) layoutInflater.inflate(
R.layout.tv_pip_custom_control, mView, false);
- mView.addView(buttonView);
- mCustomButtonViews.add(buttonView);
- }
+ mView.addView(button);
+ mAdditionalButtons.add(button);
- // Update the visibility of all views
- for (int i = 0; i < mCustomButtonViews.size(); i++) {
- mCustomButtonViews.get(i).setVisibility(
- i < mCustomActions.size() ? View.VISIBLE : View.GONE);
+ buttonsNumber++;
}
-
- // Update the state and visibility of the action buttons, and hide the rest
- for (int i = 0; i < mCustomActions.size(); i++) {
- final RemoteAction action = mCustomActions.get(i);
- PipControlButtonView actionView = mCustomButtonViews.get(i);
-
- // TODO: Check if the action drawable has changed before we reload it
- action.getIcon().loadDrawableAsync(mView.getContext(), d -> {
- d.setTint(Color.WHITE);
- actionView.setImageDrawable(d);
- }, mHandler);
- actionView.setText(action.getContentDescription());
- if (action.isEnabled()) {
- actionView.setOnClickListener(v -> {
- try {
- action.getActionIntent().send();
- } catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "Failed to send action", e);
- }
- });
- }
- actionView.setEnabled(action.isEnabled());
- actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
+ } else if (actionsNumber < buttonsNumber) {
+ // Hide buttons until we as many as the actions.
+ while (actionsNumber < buttonsNumber) {
+ final View button = mAdditionalButtons.get(buttonsNumber - 1);
+ button.setVisibility(View.GONE);
+ button.setOnClickListener(null);
+
+ buttonsNumber--;
}
+ }
- // Hide the media session buttons
- mPlayPauseButtonView.setVisibility(View.GONE);
- } else {
- AtomicInteger state = new AtomicInteger(PipController.STATE_UNKNOWN);
- state.set(mPipController.getPlaybackState());
- if (state.get() == PipController.STATE_UNKNOWN
- || state.get() == PipController.PLAYBACK_STATE_UNAVAILABLE) {
- mPlayPauseButtonView.setVisibility(View.GONE);
- } else {
- mPlayPauseButtonView.setVisibility(View.VISIBLE);
- if (state.get() == PipController.PLAYBACK_STATE_PLAYING) {
- mPlayPauseButtonView.setImageResource(R.drawable.pip_ic_pause_white);
- mPlayPauseButtonView.setText(R.string.pip_pause);
- } else {
- mPlayPauseButtonView.setImageResource(R.drawable.pip_ic_play_arrow_white);
- mPlayPauseButtonView.setText(R.string.pip_play);
+ // "Assign" actions to the buttons.
+ for (int index = 0; index < actionsNumber; index++) {
+ final RemoteAction action = actionsToDisplay.get(index);
+ final PipControlButtonView button = mAdditionalButtons.get(index);
+ button.setVisibility(View.VISIBLE); // Ensure the button is visible.
+ button.setText(action.getContentDescription());
+ button.setEnabled(action.isEnabled());
+ button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
+ button.setOnClickListener(v -> {
+ try {
+ action.getActionIntent().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Failed to send action", e);
}
- }
+ });
- // Hide all the custom action buttons
- for (int i = 0; i < mCustomButtonViews.size(); i++) {
- mCustomButtonViews.get(i).setVisibility(View.GONE);
- }
+ action.getIcon().loadDrawableAsync(mContext, drawable -> {
+ drawable.setTint(Color.WHITE);
+ button.setImageDrawable(drawable);
+ }, mUiThreadHandler);
}
}
-
-
- /**
- * Sets the {@link Listener} to listen user actions.
- */
- public void setListener(Listener listener) {
- mListener = listener;
- }
-
-
- /**
- * Updates the set of activity-defined actions.
- */
- public void setActions(List<? extends RemoteAction> actions) {
- mCustomActions.clear();
- mCustomActions.addAll(actions);
- updateUserActions();
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
index e185a9604449..d2270c278161 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
@@ -56,8 +56,7 @@ public class PipMenuActivity extends Activity implements PipController.Listener
}
setContentView(R.layout.tv_pip_menu);
mPipControlsViewController = new PipControlsViewController(
- findViewById(R.id.pip_controls), sPipController,
- getLayoutInflater(), getApplicationContext().getMainThreadHandler());
+ findViewById(R.id.pip_controls), sPipController);
sPipController.addListener(this);
mRestorePipSizeWhenClose = true;
mFadeInAnimation = AnimatorInflater.loadAnimator(
@@ -141,7 +140,7 @@ public class PipMenuActivity extends Activity implements PipController.Listener
if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()");
boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
- mPipControlsViewController.setActions(
+ mPipControlsViewController.setCustomActions(
hasCustomActions ? actions.getList() : Collections.emptyList());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
index f5bbd23fa1d6..b30dee4f331f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
@@ -28,36 +28,33 @@ import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.PlaybackState;
import android.text.TextUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.wm.shell.R;
+import com.android.wm.shell.pip.PipMediaController;
+
+import java.util.Objects;
/**
* A notification that informs users that PIP is running and also provides PIP controls.
* <p>Once it's created, it will manage the PIP notification UI by itself except for handling
* configuration changes.
*/
-public class PipNotification {
+public class PipNotification implements PipController.Listener {
+ private static final boolean DEBUG = PipController.DEBUG;
private static final String TAG = "PipNotification";
+
private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName();
- private static final boolean DEBUG = PipController.DEBUG;
+ public static final String NOTIFICATION_CHANNEL_TVPIP = "TPP";
static final String ACTION_MENU = "PipNotification.menu";
static final String ACTION_CLOSE = "PipNotification.close";
- public static final String NOTIFICATION_CHANNEL_TVPIP = "TPP";
-
private final PackageManager mPackageManager;
-
- private final PipController mPipController;
-
private final NotificationManager mNotificationManager;
private final Notification.Builder mNotificationBuilder;
- private MediaController mMediaController;
private String mDefaultTitle;
private int mDefaultIconResId;
@@ -67,87 +64,9 @@ public class PipNotification {
private String mMediaTitle;
private Bitmap mArt;
- private PipController.Listener mPipListener = new PipController.Listener() {
- @Override
- public void onPipEntered(String packageName) {
- mPackageName = packageName;
- updateMediaControllerMetadata();
- notifyPipNotification();
- }
-
- @Override
- public void onPipActivityClosed() {
- dismissPipNotification();
- mPackageName = null;
- }
-
- @Override
- public void onShowPipMenu() {
- // no-op.
- }
-
- @Override
- public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) {
- // no-op.
- }
-
- @Override
- public void onMoveToFullscreen() {
- dismissPipNotification();
- mPackageName = null;
- }
-
- @Override
- public void onPipResizeAboutToStart() {
- // no-op.
- }
- };
-
- private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- if (updateMediaControllerMetadata() && mNotified) {
- // update notification
- notifyPipNotification();
- }
- }
-
- @Override
- public void onMetadataChanged(MediaMetadata metadata) {
- if (updateMediaControllerMetadata() && mNotified) {
- // update notification
- notifyPipNotification();
- }
- }
- };
-
- private final PipController.MediaListener mPipMediaListener =
- new PipController.MediaListener() {
- @Override
- public void onMediaControllerChanged() {
- MediaController newController = mPipController.getMediaController();
- if (newController == null || mMediaController == newController) {
- return;
- }
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaControllerCallback);
- }
- mMediaController = newController;
- if (mMediaController != null) {
- mMediaController.registerCallback(mMediaControllerCallback);
- }
- if (updateMediaControllerMetadata() && mNotified) {
- // update notification
- notifyPipNotification();
- }
- }
- };
-
- public PipNotification(Context context, PipController pipController) {
+ public PipNotification(Context context, PipMediaController pipMediaController) {
mPackageManager = context.getPackageManager();
-
- mNotificationManager = (NotificationManager) context.getSystemService(
- Context.NOTIFICATION_SERVICE);
+ mNotificationManager = context.getSystemService(NotificationManager.class);
mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL_TVPIP)
.setLocalOnly(true)
@@ -157,13 +76,51 @@ public class PipNotification {
.setContentIntent(createPendingIntent(context, ACTION_MENU))
.setDeleteIntent(createPendingIntent(context, ACTION_CLOSE)));
- mPipController = pipController;
- pipController.addListener(mPipListener);
- pipController.addMediaListener(mPipMediaListener);
+ pipMediaController.addMetadataListener(this::onMediaMetadataChanged);
onConfigurationChanged(context);
}
+ @Override
+ public void onPipEntered(String packageName) {
+ mPackageName = packageName;
+ notifyPipNotification();
+ }
+
+ @Override
+ public void onPipActivityClosed() {
+ dismissPipNotification();
+ mPackageName = null;
+ }
+
+ @Override
+ public void onShowPipMenu() {
+ // no-op.
+ }
+
+ @Override
+ public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) {
+ // no-op.
+ }
+
+ @Override
+ public void onMoveToFullscreen() {
+ dismissPipNotification();
+ mPackageName = null;
+ }
+
+ @Override
+ public void onPipResizeAboutToStart() {
+ // no-op.
+ }
+
+ private void onMediaMetadataChanged(MediaMetadata metadata) {
+ if (updateMediaControllerMetadata(metadata) && mNotified) {
+ // update notification
+ notifyPipNotification();
+ }
+ }
+
/**
* Called by {@link PipController} when the configuration is changed.
*/
@@ -199,28 +156,28 @@ public class PipNotification {
mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
}
- private boolean updateMediaControllerMetadata() {
+ private boolean updateMediaControllerMetadata(MediaMetadata metadata) {
String title = null;
Bitmap art = null;
- if (mPipController.getMediaController() != null) {
- MediaMetadata metadata = mPipController.getMediaController().getMetadata();
- if (metadata != null) {
- title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
- if (TextUtils.isEmpty(title)) {
- title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
- }
- art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- if (art == null) {
- art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
- }
+ if (metadata != null) {
+ title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
+ if (TextUtils.isEmpty(title)) {
+ title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
+ }
+ art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ if (art == null) {
+ art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
}
}
- if (!TextUtils.equals(title, mMediaTitle) || art != mArt) {
- mMediaTitle = title;
- mArt = art;
- return true;
+
+ if (TextUtils.equals(title, mMediaTitle) && Objects.equals(art, mArt)) {
+ return false;
}
- return false;
+
+ mMediaTitle = title;
+ mArt = art;
+
+ return true;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 39a96a291c86..1d10a84c53b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -69,11 +69,13 @@ public class PipTaskOrganizerTest extends PipTestCase {
private PipBoundsState mPipBoundsState;
private ComponentName mComponent1;
+ private ComponentName mComponent2;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mComponent1 = new ComponentName(mContext, "component1");
+ mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState();
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState,
mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen,
@@ -101,29 +103,57 @@ public class PipTaskOrganizerTest extends PipTestCase {
}
@Test
+ public void startSwipePipToHome_updatesLastPipComponentName() {
+ mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, null);
+
+ assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
+ }
+
+ @Test
public void onTaskAppeared_updatesAspectRatio() {
final Rational aspectRatio = new Rational(2, 1);
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(
+ mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(aspectRatio)), null /* leash */);
assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
}
@Test
+ public void onTaskAppeared_updatesLastPipComponentName() {
+ mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)),
+ null /* leash */);
+
+ assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
+ }
+
+ @Test
public void onTaskInfoChanged_updatesAspectRatioIfChanged() {
final Rational startAspectRatio = new Rational(2, 1);
final Rational newAspectRatio = new Rational(1, 2);
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(
+ mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(startAspectRatio)), null /* leash */);
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(createPipParams(newAspectRatio)));
+ mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+ createPipParams(newAspectRatio)));
assertEquals(newAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
}
+ @Test
+ public void onTaskInfoChanged_updatesLastPipComponentName() {
+ mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ createPipParams(null)), null /* leash */);
+
+ mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
+ createPipParams(null)));
+
+ assertEquals(mComponent2, mPipBoundsState.getLastPipComponentName());
+ }
+
private void preparePipTaskOrg() {
final DisplayInfo info = new DisplayInfo();
+ mPipBoundsState.setDisplayInfo(info);
when(mMockPipBoundsHandler.getDestinationBounds(any(), any())).thenReturn(new Rect());
when(mMockPipBoundsHandler.getDestinationBounds(any(), any(), anyBoolean()))
.thenReturn(new Rect());
@@ -133,10 +163,12 @@ public class PipTaskOrganizerTest extends PipTestCase {
doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
}
- private static ActivityManager.RunningTaskInfo createTaskInfo(PictureInPictureParams params) {
+ private static ActivityManager.RunningTaskInfo createTaskInfo(
+ ComponentName componentName, PictureInPictureParams params) {
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.token = mock(WindowContainerToken.class);
info.pictureInPictureParams = params;
+ info.topActivity = componentName;
return info;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index de9d8648f43c..5f0f1964814f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -20,11 +20,14 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
@@ -34,6 +37,7 @@ import android.testing.TestableLooper;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsHandler;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
@@ -64,6 +68,7 @@ public class PipControllerTest extends PipTestCase {
@Mock private PipTouchHandler mMockPipTouchHandler;
@Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
@Mock private PipBoundsState mMockPipBoundsState;
+ @Mock private ShellExecutor mMockExecutor;
@Before
public void setUp() throws RemoteException {
@@ -71,7 +76,11 @@ public class PipControllerTest extends PipTestCase {
mPipController = new PipController(mContext, mMockDisplayController,
mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState,
mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
- mMockPipTouchHandler, mMockWindowManagerShellWrapper);
+ mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockExecutor);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mMockExecutor).execute(any());
}
@Test
@@ -99,6 +108,27 @@ public class PipControllerTest extends PipTestCase {
assertNull(PipController.create(spyContext, mMockDisplayController,
mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState,
mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
- mMockPipTouchHandler, mMockWindowManagerShellWrapper));
+ mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockExecutor));
+ }
+
+ @Test
+ public void onActivityHidden_isLastPipComponentName_clearLastPipComponent() {
+ final ComponentName component1 = new ComponentName(mContext, "component1");
+ when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1);
+
+ mPipController.mPinnedStackListener.onActivityHidden(component1);
+
+ verify(mMockPipBoundsState).setLastPipComponentName(null);
+ }
+
+ @Test
+ public void onActivityHidden_isNotLastPipComponentName_lastPipComponentNotCleared() {
+ final ComponentName component1 = new ComponentName(mContext, "component1");
+ final ComponentName component2 = new ComponentName(mContext, "component2");
+ when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1);
+
+ mPipController.mPinnedStackListener.onActivityHidden(component2);
+
+ verify(mMockPipBoundsState, never()).setLastPipComponentName(null);
}
}
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index 9d83e491fdc1..3a99d41448f2 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -7,6 +7,8 @@ cc_library_host_static {
static_libs: [
"libbase",
+ "libmath",
+ "libutils",
],
srcs: [
@@ -24,6 +26,7 @@ cc_library_host_static {
"frameworks/native/libs/nativebase/include",
"frameworks/native/libs/nativewindow/include",
"frameworks/native/libs/arect/include",
+ "frameworks/native/libs/ui/include_private",
],
export_include_dirs: ["."],
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index cd2af1b85f50..bd46ffdf7e57 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -901,8 +901,59 @@ public final class GnssMeasurement implements Parcelable {
/**
* Gets 'Accumulated Delta Range' state.
*
- * <p>It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a
- * cycle slip (indicating 'loss of lock').
+ * <p>This indicates the state of the {@link #getAccumulatedDeltaRangeMeters()} measurement. See
+ * the table below for a detailed interpretation of each state.
+ *
+ * <table border="1">
+ * <thead>
+ * <tr>
+ * <th>ADR_STATE</th>
+ * <th>Time of relevance</th>
+ * <th>Interpretation</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>UNKNOWN</td>
+ * <td>ADR(t)</td>
+ * <td>No valid carrier phase information is available at time t.</td>
+ * </tr>
+ * <tr>
+ * <td>VALID</td>
+ * <td>ADR(t)</td>
+ * <td>Valid carrier phase information is available at time t. This indicates that this
+ * measurement can be used as a reference for future measurements. However, to compare it to
+ * previous measurements to compute delta range, other bits should be checked. Specifically,
+ * it can be used for delta range computation if it is valid and has no reset or cycle slip at
+ * this epoch i.e. if VALID_BIT == 1 && CYCLE_SLIP_BIT == 0 && RESET_BIT == 0.</td>
+ * </tr>
+ * <tr>
+ * <td>RESET</td>
+ * <td>ADR(t) - ADR(t-1)</td>
+ * <td>Carrier phase accumulation has been restarted between current time t and previous time
+ * t-1. This indicates that this measurement can be used as a reference for future measurements,
+ * but it should not be compared to previous measurements to compute delta range.</td>
+ * </tr>
+ * <tr>
+ * <td>CYCLE_SLIP</td>
+ * <td>ADR(t) - ADR(t-1)</td>
+ * <td>Cycle slip(s) have been detected between the current time t and previous time t-1. This
+ * indicates that this measurement can be used as a reference for future measurements. Clients
+ * can use a measurement with a cycle slip to compute delta range against previous measurements
+ * at their own risk.</td>
+ * </tr>
+ * <tr>
+ * <td>HALF_CYCLE_RESOLVED</td>
+ * <td>ADR(t)</td>
+ * <td>Half cycle ambiguity is resolved at time t.</td>
+ * </tr>
+ * <tr>
+ * <td>HALF_CYCLE_REPORTED</td>
+ * <td>ADR(t)</td>
+ * <td>Half cycle ambiguity is reported at time t.</td>
+ * </tr>
+ * </tbody>
+ * </table>
*/
@AdrState
public int getAccumulatedDeltaRangeState() {
diff --git a/media/java/android/media/ApplicationMediaCapabilities.java b/media/java/android/media/ApplicationMediaCapabilities.java
new file mode 100644
index 000000000000..4b28553cce90
--- /dev/null
+++ b/media/java/android/media/ApplicationMediaCapabilities.java
@@ -0,0 +1,153 @@
+/*
+ * 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.media;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * ApplicationMediaCapabilities is an immutable class that encapsulates an application's
+ * capabilities of handling advanced media formats.
+ *
+ * The ApplicationMediaCapabilities class is used by the platform to to represent an application's
+ * media capabilities as defined in their manifest(TODO: Add link) in order to determine
+ * whether modern media files need to be transcoded for that application (TODO: Add link).
+ *
+ * ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
+ * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
+ * control over the transcoding that is built into the platform. ApplicationMediaCapabilities
+ * provided by applications at runtime like this override the default manifest capabilities for that
+ * media access.
+ *
+ * TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added.
+ * TODO(hkuang): Add a link to seamless transcoding detail when it is published
+ * TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList
+ *
+ * @hide
+ */
+public final class ApplicationMediaCapabilities implements Parcelable {
+ private static final String TAG = "ApplicationMediaCapabilities";
+
+ /** Whether handling of HEVC video is supported. */
+ private final boolean mIsHevcSupported;
+
+ /** Whether handling of slow-motion video is supported. */
+ private final boolean mIsSlowMotionSupported;
+
+ /** Whether handling of high dynamic range video is supported. */
+ private final boolean mIsHdrSupported;
+
+ private ApplicationMediaCapabilities(Builder b) {
+ mIsHevcSupported = b.mIsHevcSupported;
+ mIsHdrSupported = b.mIsHdrSupported;
+ mIsSlowMotionSupported = b.mIsSlowMotionSupported;
+ }
+
+ /** Whether handling of HEVC video is supported. */
+ public boolean isHevcSupported() {
+ return mIsHevcSupported;
+ }
+
+ /** Whether handling of slow-motion video is supported. */
+ public boolean isSlowMotionSupported() {
+ return mIsSlowMotionSupported;
+ }
+
+ /** Whether handling of high dynamic range video is supported. */
+ public boolean isHdrSupported() {
+ return mIsHdrSupported;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mIsHevcSupported);
+ dest.writeBoolean(mIsHdrSupported);
+ dest.writeBoolean(mIsSlowMotionSupported);
+ }
+
+ /**
+ * Builder class for {@link ApplicationMediaCapabilities} objects.
+ * Use this class to configure and create an ApplicationMediaCapabilities instance. Builder
+ * could be created from an existing ApplicationMediaCapabilities object, from a xml file or
+ * MediaCodecList.
+ * //TODO(hkuang): Add xml parsing support to the builder.
+ */
+ public final static class Builder {
+ private boolean mIsHevcSupported = false;
+ private boolean mIsHdrSupported = false;
+ private boolean mIsSlowMotionSupported = false;
+
+ /**
+ * Constructs a new Builder with all the supports default to false.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Builds a {@link ApplicationMediaCapabilities} object.
+ *
+ * @return a new {@link ApplicationMediaCapabilities} instance successfully initialized
+ * with all the parameters set on this <code>Builder</code>.
+ * @throws UnsupportedOperationException if the parameters set on the
+ * <code>Builder</code> were incompatible, or if they are not supported by the
+ * device.
+ */
+ @NonNull
+ public ApplicationMediaCapabilities build() {
+ if (mIsHdrSupported && !mIsHevcSupported) {
+ throw new UnsupportedOperationException("Must also support HEVC if support HDR.");
+ }
+ return new ApplicationMediaCapabilities(this);
+ }
+
+ /**
+ * Sets whether supports HEVC encoded video.
+ */
+ @NonNull
+ public Builder setHevcSupported(boolean hevcSupported) {
+ mIsHevcSupported = hevcSupported;
+ return this;
+ }
+
+ /**
+ * Sets whether supports high dynamic range video.
+ */
+ @NonNull
+ public Builder setHdrSupported(boolean hdrSupported) {
+ mIsHdrSupported = hdrSupported;
+ return this;
+ }
+
+ /**
+ * Sets whether supports slow-motion video.
+ */
+ @NonNull
+ public Builder setSlowMotionSupported(boolean slowMotionSupported) {
+ mIsSlowMotionSupported = slowMotionSupported;
+ return this;
+ }
+ }
+}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java
new file mode 100644
index 000000000000..8a668c670d1e
--- /dev/null
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/ApplicationMediaCapabilitiesTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.mediatranscodingtest;
+
+/*
+ * Test for ApplicationMediaCapabilities in the media framework.
+ *
+ * To run this test suite:
+ make frameworks/base/media/tests/MediaTranscodingTest
+ make mediatranscodingtest
+
+ adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
+
+ adb shell am instrument -e class \
+ com.android.mediatranscodingtest.MediaCapabilityTest \
+ -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+ *
+ */
+
+import static org.testng.Assert.assertThrows;
+
+import android.media.ApplicationMediaCapabilities;
+import android.test.ActivityInstrumentationTestCase2;
+
+import org.junit.Test;
+
+public class ApplicationMediaCapabilitiesTest extends
+ ActivityInstrumentationTestCase2<MediaTranscodingTest> {
+ private static final String TAG = "MediaCapabilityTest";
+
+ public ApplicationMediaCapabilitiesTest() {
+ super("com.android.MediaCapabilityTest", MediaTranscodingTest.class);
+ }
+
+ @Test
+ public void testSetSupportHevc() throws Exception {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().setHevcSupported(true).build();
+ assertTrue(capability.isHevcSupported());
+
+ ApplicationMediaCapabilities capability2 =
+ new ApplicationMediaCapabilities.Builder().setHevcSupported(false).build();
+ assertFalse(capability2.isHevcSupported());
+ }
+
+ @Test
+ public void testSetSupportHdr() throws Exception {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().setHdrSupported(true).setHevcSupported(
+ true).build();
+ assertEquals(true, capability.isHdrSupported());
+ }
+
+ @Test
+ public void testSetSupportSlowMotion() throws Exception {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().setSlowMotionSupported(
+ true).build();
+ assertTrue(capability.isSlowMotionSupported());
+ }
+
+ @Test
+ public void testBuilder() throws Exception {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().setHdrSupported(
+ true).setHevcSupported(true).setSlowMotionSupported(true).build();
+ assertTrue(capability.isHdrSupported());
+ assertTrue(capability.isSlowMotionSupported());
+ assertTrue(capability.isSlowMotionSupported());
+ }
+
+ @Test
+ public void testSupportHdrWithoutSupportHevc() throws Exception {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ ApplicationMediaCapabilities capability =
+ new ApplicationMediaCapabilities.Builder().setHdrSupported(
+ true).setHevcSupported(false).build();
+ });
+ }
+}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
index bd1551f352f4..76050ac4aba1 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
@@ -37,6 +37,7 @@ public class MediaTranscodingTestRunner extends InstrumentationTestRunner {
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
suite.addTestSuite(MediaTranscodeManagerDiedTest.class);
+ suite.addTestSuite(ApplicationMediaCapabilitiesTest.class);
suite.addTestSuite(MediaTranscodeManagerTest.class);
suite.addTestSuite(MediaTranscodingBenchmark.class);
return suite;
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 41a9057c005a..16a5f5e82f21 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -42276,13 +42276,13 @@ package android.service.notification {
method public boolean canBubble();
method public boolean canShowBadge();
method public android.app.NotificationChannel getChannel();
+ method @Nullable public android.content.pm.ShortcutInfo getConversationShortcutInfo();
method public int getImportance();
method public CharSequence getImportanceExplanation();
method public String getKey();
method public long getLastAudiblyAlertedMillis();
method public String getOverrideGroupKey();
method public int getRank();
- method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
method public int getSuppressedVisualEffects();
@@ -55346,8 +55346,8 @@ package android.view.inputmethod {
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
- method public CharSequence getTextAfterCursor(int, int);
- method public CharSequence getTextBeforeCursor(int, int);
+ method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
+ method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
@@ -55573,8 +55573,8 @@ package android.view.inputmethod {
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
- method public CharSequence getTextAfterCursor(int, int);
- method public CharSequence getTextBeforeCursor(int, int);
+ method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
+ method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
@@ -55608,8 +55608,8 @@ package android.view.inputmethod {
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
- method public CharSequence getTextAfterCursor(int, int);
- method public CharSequence getTextBeforeCursor(int, int);
+ method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
+ method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index ae6ecc763aef..06e6d9ccbbf0 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -130,6 +130,7 @@ package android.provider {
public final class DeviceConfig {
field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
+ field public static final String NAMESPACE_APP_STANDBY = "app_standby";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
}
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 9f9658308838..eea50be6da1d 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -682,7 +682,7 @@ package android.app {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
- method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
@@ -2142,6 +2142,7 @@ package android.content.pm {
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
+ method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int);
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
@@ -2220,11 +2221,16 @@ package android.content.pm {
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
+ field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
field public static final int MODULE_APEX_NAME = 1; // 0x1
field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
field public static final int RESTRICTION_NONE = 0; // 0x0
+ field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0; // 0x0
+ field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1; // 0x1
+ field public static final int SYSTEM_APP_STATE_INSTALLED = 2; // 0x2
+ field public static final int SYSTEM_APP_STATE_UNINSTALLED = 3; // 0x3
}
public abstract static class PackageManager.DexModuleRegisterCallback {
@@ -8883,6 +8889,7 @@ package android.service.autofill {
public static final class Dataset.Builder {
ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setContent(@NonNull android.view.autofill.AutofillId, @Nullable android.content.ClipData);
method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
}
@@ -10887,7 +10894,8 @@ package android.telephony.data {
method public int getMtuV6();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
method public int getProtocolType();
- method public int getSuggestedRetryTime();
+ method public long getRetryIntervalMillis();
+ method @Deprecated public int getSuggestedRetryTime();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1
@@ -10899,6 +10907,7 @@ package android.telephony.data {
field public static final int LINK_STATUS_DORMANT = 1; // 0x1
field public static final int LINK_STATUS_INACTIVE = 0; // 0x0
field public static final int LINK_STATUS_UNKNOWN = -1; // 0xffffffff
+ field public static final int RETRY_INTERVAL_UNDEFINED = -1; // 0xffffffff
}
public static final class DataCallResponse.Builder {
@@ -10917,7 +10926,8 @@ package android.telephony.data {
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryIntervalMillis(long);
+ method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
}
public final class DataProfile implements android.os.Parcelable {
diff --git a/packages/CarSystemUI/res/values-ar/strings.xml b/packages/CarSystemUI/res/values-ar/strings.xml
index 61a08a4eae49..d9abb0af5608 100644
--- a/packages/CarSystemUI/res/values-ar/strings.xml
+++ b/packages/CarSystemUI/res/values-ar/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"يمكن لأي مستخدم تحديث التطبيقات لجميع المستخدمين الآخرين."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"جارٍ التحميل"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"جارٍ تحميل الملف الشخصي الجديد للمستخدم (من <xliff:g id="FROM_USER">%1$d</xliff:g> إلى <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"إغلاق"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-bn/strings.xml b/packages/CarSystemUI/res/values-bn/strings.xml
index a45e66e8c2f2..5664cc12e109 100644
--- a/packages/CarSystemUI/res/values-bn/strings.xml
+++ b/packages/CarSystemUI/res/values-bn/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"যেকোনও ব্যবহারকারী বাকি সব ব্যবহারকারীর জন্য অ্যাপ আপডেট করতে পারবেন।"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"লোড হচ্ছে"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ব্যবহারকারীর প্রোফাইল লোড করা হচ্ছে (<xliff:g id="FROM_USER">%1$d</xliff:g> থেকে <xliff:g id="TO_USER">%2$d</xliff:g>-এ)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"বন্ধ করুন"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-de/strings.xml b/packages/CarSystemUI/res/values-de/strings.xml
index 131dee19b0ca..e2437f0c55cb 100644
--- a/packages/CarSystemUI/res/values-de/strings.xml
+++ b/packages/CarSystemUI/res/values-de/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Jeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Wird geladen"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nutzer wird geladen (von <xliff:g id="FROM_USER">%1$d</xliff:g> bis <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Schließen"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-eu/strings.xml b/packages/CarSystemUI/res/values-eu/strings.xml
index 5d2ca3548af2..9139e650187e 100644
--- a/packages/CarSystemUI/res/values-eu/strings.xml
+++ b/packages/CarSystemUI/res/values-eu/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Edozein erabiltzailek egunera ditzake beste erabiltzaile guztien aplikazioak."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Kargatzen"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Erabiltzailea kargatzen (<xliff:g id="FROM_USER">%1$d</xliff:g> izatetik<xliff:g id="TO_USER">%2$d</xliff:g> izatera igaroko da)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Itxi"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-gl/strings.xml b/packages/CarSystemUI/res/values-gl/strings.xml
index e4586cc17a73..e77df4f50272 100644
--- a/packages/CarSystemUI/res/values-gl/strings.xml
+++ b/packages/CarSystemUI/res/values-gl/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Calquera usuario pode actualizar as aplicacións para o resto dos usuarios."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (do <xliff:g id="FROM_USER">%1$d</xliff:g> ao <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Pechar"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-gu/strings.xml b/packages/CarSystemUI/res/values-gu/strings.xml
index ba884e410187..174d7a724240 100644
--- a/packages/CarSystemUI/res/values-gu/strings.xml
+++ b/packages/CarSystemUI/res/values-gu/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"કોઈપણ વપરાશકર્તા અન્ય બધા વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"લોડ કરી રહ્યાં છીએ"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"વપરાશકર્તાને લોડ કરી રહ્યાં છીએ (<xliff:g id="FROM_USER">%1$d</xliff:g>માંથી <xliff:g id="TO_USER">%2$d</xliff:g>માં)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"બંધ કરો"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-kn/strings.xml b/packages/CarSystemUI/res/values-kn/strings.xml
index 34a83553d5c0..b9667df3afb3 100644
--- a/packages/CarSystemUI/res/values-kn/strings.xml
+++ b/packages/CarSystemUI/res/values-kn/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗಾಗಿ ಆ್ಯಪ್‌ಗಳನ್ನು ಅಪ್‌ಡೇಟ್‌ ಮಾಡಬಹುದು."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"ಲೋಡ್ ಆಗುತ್ತಿದೆ"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ಬಳಕೆದಾರರನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ (<xliff:g id="FROM_USER">%1$d</xliff:g> ನಿಂದ <xliff:g id="TO_USER">%2$d</xliff:g> ವರೆಗೆ)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ಮುಚ್ಚಿರಿ"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ml/strings.xml b/packages/CarSystemUI/res/values-ml/strings.xml
index 3bc7557b5484..613ea59874bc 100644
--- a/packages/CarSystemUI/res/values-ml/strings.xml
+++ b/packages/CarSystemUI/res/values-ml/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ഏതൊരു ഉപയോക്താവിനും മറ്റെല്ലാ ഉപയോക്താക്കൾക്കുമായി ആപ്പുകൾ അപ്‌ഡേറ്റ് ചെയ്യാനാവും."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"ലോഡ് ചെയ്യുന്നു"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ഉപയോക്തൃ പ്രൊഫൈൽ ലോഡ് ചെയ്യുന്നു (<xliff:g id="FROM_USER">%1$d</xliff:g> എന്നതിൽ നിന്ന് <xliff:g id="TO_USER">%2$d</xliff:g> എന്നതിലേക്ക്)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"അടയ്ക്കുക"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-mr/strings.xml b/packages/CarSystemUI/res/values-mr/strings.xml
index fdbab4fbc4f2..b1c8a72a04df 100644
--- a/packages/CarSystemUI/res/values-mr/strings.xml
+++ b/packages/CarSystemUI/res/values-mr/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"कोणताही वापरकर्ता इतर सर्व वापरकर्त्यांसाठी अ‍ॅप्स अपडेट करू शकतो."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"लोड करत आहे"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"वापरकर्ता लोड करत आहे (<xliff:g id="FROM_USER">%1$d</xliff:g> पासून <xliff:g id="TO_USER">%2$d</xliff:g> पर्यंत)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"बंद करा"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ne/strings.xml b/packages/CarSystemUI/res/values-ne/strings.xml
index e9eb4d8bdac7..3a25d6e05db6 100644
--- a/packages/CarSystemUI/res/values-ne/strings.xml
+++ b/packages/CarSystemUI/res/values-ne/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"सबै प्रयोगकर्ताले अन्य प्रयोगकर्ताका अनुप्रयोगहरू अद्यावधिक गर्न सक्छन्।"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"लोड गरिँदै"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"प्रयोगकर्तासम्बन्धी जानकारी लोड गरिँदै (<xliff:g id="FROM_USER">%1$d</xliff:g> बाट <xliff:g id="TO_USER">%2$d</xliff:g> मा)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"बन्द गर्नुहोस्"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-or/strings.xml b/packages/CarSystemUI/res/values-or/strings.xml
index 58f59e4c4dbf..4168d5a109b8 100644
--- a/packages/CarSystemUI/res/values-or/strings.xml
+++ b/packages/CarSystemUI/res/values-or/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ଯେ କୌଣସି ଉପଯୋଗକର୍ତ୍ତା ଅନ୍ୟ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଆପଗୁଡ଼ିକୁ ଅପଡେଟ୍ କରିପାରିବେ।"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"ଲୋଡ୍ କରାଯାଉଛି"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଲୋଡ୍ କରାଯାଉଛି (<xliff:g id="FROM_USER">%1$d</xliff:g>ଙ୍କ ଠାରୁ <xliff:g id="TO_USER">%2$d</xliff:g> ପର୍ଯ୍ୟନ୍ତ)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ବନ୍ଦ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-pa/strings.xml b/packages/CarSystemUI/res/values-pa/strings.xml
index e73e20a5fc85..3d9d3a597c07 100644
--- a/packages/CarSystemUI/res/values-pa/strings.xml
+++ b/packages/CarSystemUI/res/values-pa/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ਕੋਈ ਵੀ ਵਰਤੋਂਕਾਰ ਹੋਰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਸਕਦਾ ਹੈ।"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ਵਰਤੋਂਕਾਰ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ (<xliff:g id="FROM_USER">%1$d</xliff:g> ਤੋਂ <xliff:g id="TO_USER">%2$d</xliff:g> ਤੱਕ)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ਬੰਦ ਕਰੋ"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-te/strings.xml b/packages/CarSystemUI/res/values-te/strings.xml
index fff0845dfb6c..cf74f8053349 100644
--- a/packages/CarSystemUI/res/values-te/strings.xml
+++ b/packages/CarSystemUI/res/values-te/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ఏ యూజర్ అయినా మిగతా యూజర్‌ల కోసం యాప్‌లను అప్‌డేట్ చేయవచ్చు."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"లోడ్ అవుతోంది"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"యూజర్‌ను లోడ్ చేస్తోంది (<xliff:g id="FROM_USER">%1$d</xliff:g> నుండి <xliff:g id="TO_USER">%2$d</xliff:g> వరకు)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"మూసివేయి"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ur/strings.xml b/packages/CarSystemUI/res/values-ur/strings.xml
index ef65c7516956..abe9214181a2 100644
--- a/packages/CarSystemUI/res/values-ur/strings.xml
+++ b/packages/CarSystemUI/res/values-ur/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"کوئی بھی صارف دیگر سبھی صارفین کے لیے ایپس کو اپ ڈیٹ کر سکتا ہے۔"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"لوڈ ہو رہی ہے"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"صارف کی نئی پروفائل لوڈ ہو رہی ہے (<xliff:g id="FROM_USER">%1$d</xliff:g> سے <xliff:g id="TO_USER">%2$d</xliff:g> کو)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"بند کریں"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 9aa7cc4ae29f..bdfbf82145c7 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -19,6 +19,8 @@ package com.android.companiondevicemanager;
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static java.util.Objects.requireNonNull;
+
import android.app.Activity;
import android.companion.CompanionDeviceManager;
import android.content.Intent;
@@ -129,6 +131,11 @@ public class DeviceChooserActivity extends Activity {
}
@Override
+ public String getCallingPackage() {
+ return requireNonNull(getService().mRequest.getCallingPackage());
+ }
+
+ @Override
public void setTitle(CharSequence title) {
final TextView titleView = findViewById(R.id.title);
final int padding = getPadding(getResources());
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index f7f3cbb7d332..12505bc55b15 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -422,7 +422,7 @@ public class DynamicSystemInstallationService extends Service
private PendingIntent createPendingIntent(String action) {
Intent intent = new Intent(this, DynamicSystemInstallationService.class);
intent.setAction(action);
- return PendingIntent.getService(this, 0, intent, 0);
+ return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
private Notification buildNotification(int status, int cause) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 2674eaf4b76c..0198168f9fda 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -17,6 +17,7 @@
package com.android.packageinstaller;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
@@ -87,6 +88,8 @@ public class UninstallerActivity extends Activity {
@Override
public void onCreate(Bundle icicle) {
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
// Never restore any state, esp. never create any fragments. The data in the fragment might
// be stale, if e.g. the app was uninstalled while the activity was destroyed.
super.onCreate(null);
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 32f6aa7774a7..6e4ce03c5d93 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -473,7 +473,7 @@
<string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"Najveći"</string>
<string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Prilagodi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="content_description_menu_button" msgid="6254844309171779931">"Meni"</string>
- <string name="retail_demo_reset_message" msgid="5392824901108195463">"Unesite lozinku da izvršite vraćanje na fabričke postavke u načinu demonstracije"</string>
+ <string name="retail_demo_reset_message" msgid="5392824901108195463">"Unesite lozinku da izvršite vraćanje na fabričke postavke u načinu rada za demonstraciju"</string>
<string name="retail_demo_reset_next" msgid="3688129033843885362">"Naprijed"</string>
<string name="retail_demo_reset_title" msgid="1866911701095959800">"Potrebna je lozinka"</string>
<string name="active_input_method_subtypes" msgid="4232680535471633046">"Aktivne metode unosa"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index f6157b42312f..1d9994d52e5d 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -510,7 +510,7 @@
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
- <string name="help_label" msgid="3528360748637781274">"Ayuda y sugerencias"</string>
+ <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
<string name="storage_category" msgid="2287342585424631813">"Almacenamiento"</string>
<string name="shared_data_title" msgid="1017034836800864953">"Datos compartidos"</string>
<string name="shared_data_summary" msgid="5516326713822885652">"Ver y modificar los datos compartidos"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index b68aab46c028..8e13afd480f7 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -195,7 +195,7 @@
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Вибрати профіль"</string>
<string name="category_personal" msgid="6236798763159385225">"Особисте"</string>
- <string name="category_work" msgid="4014193632325996115">"Робота"</string>
+ <string name="category_work" msgid="4014193632325996115">"Робоче"</string>
<string name="development_settings_title" msgid="140296922921597393">"Параметри розробника"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Увімкнути параметри розробника"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Установити параметри для розробки програми"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 2fd46d94d5cc..3cbf2685af26 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -15,6 +15,9 @@ import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
@@ -308,6 +311,36 @@ public class Utils {
}
/**
+ * Create a color matrix suitable for a ColorMatrixColorFilter that modifies only the color but
+ * preserves the alpha for a given drawable
+ * @param color
+ * @return a color matrix that uses the source alpha and given color
+ */
+ public static ColorMatrix getAlphaInvariantColorMatrixForColor(@ColorInt int color) {
+ int r = Color.red(color);
+ int g = Color.green(color);
+ int b = Color.blue(color);
+
+ ColorMatrix cm = new ColorMatrix(new float[] {
+ 0, 0, 0, 0, r,
+ 0, 0, 0, 0, g,
+ 0, 0, 0, 0, b,
+ 0, 0, 0, 1, 0 });
+
+ return cm;
+ }
+
+ /**
+ * Create a ColorMatrixColorFilter to tint a drawable but retain its alpha characteristics
+ *
+ * @return a ColorMatrixColorFilter which changes the color of the output but is invariant on
+ * the source alpha
+ */
+ public static ColorFilter getAlphaInvariantColorFilterForColor(@ColorInt int color) {
+ return new ColorMatrixColorFilter(getAlphaInvariantColorMatrixForColor(color));
+ }
+
+ /**
* Determine whether a package is a "system package", in which case certain things (like
* disabling notifications or disabling the package altogether) should be disallowed.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
index a5b5312707d0..5fa04f93e993 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
@@ -108,6 +108,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int)
private val fillColorStrokePaint = Paint(Paint.ANTI_ALIAS_FLAG).also { p ->
p.color = frameColor
+ p.alpha = 255
p.isDither = true
p.strokeWidth = 5f
p.style = Paint.Style.STROKE
@@ -145,7 +146,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int)
// Only used if dualTone is set to true
private val dualToneBackgroundFill = Paint(Paint.ANTI_ALIAS_FLAG).also { p ->
p.color = frameColor
- p.alpha = 255
+ p.alpha = 85 // ~0.3 alpha by default
p.isDither = true
p.strokeWidth = 0f
p.style = Paint.Style.FILL_AND_STROKE
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1a2c2c8ac2e9..38ff447a71b5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -52,6 +52,8 @@ class SettingsProtoDumpUtil {
ConfigSettingsProto.ALARM_MANAGER_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_APP_COMPAT,
ConfigSettingsProto.APP_COMPAT_SETTINGS);
+ namespaceToFieldMap.put(DeviceConfig.NAMESPACE_APP_STANDBY,
+ ConfigSettingsProto.APP_STANDBY_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_AUTOFILL,
ConfigSettingsProto.AUTOFILL_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_BLOBSTORE,
@@ -220,9 +222,6 @@ class SettingsProtoDumpUtil {
final long appToken = p.start(GlobalSettingsProto.APP);
dumpSetting(s, p,
- Settings.Global.APP_IDLE_CONSTANTS,
- GlobalSettingsProto.App.IDLE_CONSTANTS);
- dumpSetting(s, p,
Settings.Global.APP_STANDBY_ENABLED,
GlobalSettingsProto.App.STANDBY_ENABLED);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6dd2936cfb00..36213a020783 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3342,7 +3342,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 193;
+ private static final int SETTINGS_VERSION = 194;
private final int mUserId;
@@ -4754,6 +4754,13 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 193;
}
+ if (currentVersion == 193) {
+ // Version 193: remove obsolete LOCATION_PROVIDERS_ALLOWED settings
+ getSecureSettingsLocked(userId).deleteSettingLocked(
+ Secure.LOCATION_PROVIDERS_ALLOWED);
+ currentVersion = 194;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index de4325098a7f..baa266a6e5cd 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -127,7 +127,6 @@ public class SettingsBackupTest {
Settings.Global.APN_DB_UPDATE_CONTENT_URL,
Settings.Global.APN_DB_UPDATE_METADATA_URL,
Settings.Global.APP_BINDING_CONSTANTS,
- Settings.Global.APP_IDLE_CONSTANTS,
Settings.Global.APP_OPS_CONSTANTS,
Settings.Global.APP_STANDBY_ENABLED,
Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE,
@@ -443,6 +442,7 @@ public class SettingsBackupTest {
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
Settings.Global.SHOW_PEOPLE_SPACE,
Settings.Global.SHOW_NEW_LOCKSCREEN,
+ Settings.Global.SHOW_NEW_NOTIF_DISMISS,
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
Settings.Global.SHOW_TEMPERATURE_WARNING,
Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
@@ -745,7 +745,6 @@ public class SettingsBackupTest {
Settings.Secure.FACE_UNLOCK_RE_ENROLL,
Settings.Secure.TAP_GESTURE,
Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable
- Settings.Secure.WINDOW_MAGNIFICATION,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER,
Settings.Secure.SUPPRESS_DOZE,
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 02751e27874d..5200d95d8d58 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -792,7 +792,7 @@ public class BugreportProgressService extends Service {
intent.setClass(context, BugreportProgressService.class);
intent.putExtra(EXTRA_ID, info.id);
return PendingIntent.getService(context, info.id, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
/**
@@ -1252,7 +1252,7 @@ public class BugreportProgressService extends Service {
.setTicker(title)
.setContentText(content)
.setContentIntent(PendingIntent.getService(mContext, info.id, shareIntent,
- PendingIntent.FLAG_UPDATE_CURRENT))
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.setOnlyAlertOnce(false)
.setDeleteIntent(newCancelIntent(mContext, info));
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index 31a33fbfc308..ccd235d54c0b 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -5,6 +5,4 @@
android:id="@+id/udfps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- systemui:sensorRadius="130px"
- systemui:sensorCenterY="1636px"
systemui:sensorTouchAreaCoefficient="0.5"/>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index a62502965dd2..78d92c4b47e2 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -158,8 +158,6 @@
</declare-styleable>
<declare-styleable name="UdfpsView">
- <attr name="sensorRadius" format="dimension"/>
- <attr name="sensorCenterY" format="dimension"/>
<attr name="sensorPressureCoefficient" format="float"/>
<attr name="sensorTouchAreaCoefficient" format="float"/>
</declare-styleable>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index f2bb4907ee8f..7d3135ac1937 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -155,6 +155,7 @@
<item type="id" name="scale_y_dynamicanimation_tag"/>
<item type="id" name="physics_animator_tag"/>
<item type="id" name="target_animator_tag" />
+ <item type="id" name="reorder_animator_tag"/>
<!-- Global Actions Menu -->
<item type="id" name="global_actions_view" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1a98c206eb0d..9beb7db40096 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2102,7 +2102,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (isEncryptedOrLockdown(userId)) {
mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback,
- userId, null /* surface */);
+ userId);
} else {
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback, null /* handler */, userId);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 8ac6edb9dfa2..cf576dd6b964 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -37,7 +37,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -308,7 +307,6 @@ public class Dependency {
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
- @Inject Lazy<Bubbles> mBubbles;
@Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
@@ -506,7 +504,6 @@ public class Dependency {
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
mRemoteInputQuickSettingsDisabler::get);
- mProviders.put(Bubbles.class, mBubbles::get);
mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index e709830342b5..bf42a60ac033 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -34,6 +34,7 @@ import android.provider.Settings;
import android.util.Log;
import android.util.TimingsTraceLog;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.systemui.dagger.ContextComponentHelper;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
@@ -69,6 +70,8 @@ public class SystemUIApplication extends Application implements
public SystemUIApplication() {
super();
Log.v(TAG, "SystemUIApplication constructed.");
+ // SysUI may be building without protolog preprocessing in some cases
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 187c31f43a64..4f9d84de36a2 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -101,11 +101,13 @@ public class SystemUIFactory {
if (initializeComponents) {
// Only initialize when not starting from tests since this currently initializes some
// components that shouldn't be run in the test environment
- builder = builder.setPip(mWMComponent.getPip())
+ builder = prepareSysUIComponentBuilder(builder, mWMComponent)
+ .setPip(mWMComponent.getPip())
.setSplitScreen(mWMComponent.getSplitScreen())
.setOneHanded(mWMComponent.getOneHanded())
.setShellDump(mWMComponent.getShellDump());
} else {
+ // TODO: Call on prepareSysUIComponentBuilder but not with real components.
builder = builder.setPip(Optional.ofNullable(null))
.setSplitScreen(Optional.ofNullable(null))
.setOneHanded(Optional.ofNullable(null))
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index e3b00495f3dc..3c2e00869ab8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -78,7 +78,7 @@ class UdfpsController implements DozeReceiver {
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting
- final int mUdfpsSensorId;
+ final FingerprintSensorPropertiesInternal mSensorProps;
private final WindowManager mWindowManager;
private final SystemSettings mSystemSettings;
private final DelayableExecutor mFgExecutor;
@@ -180,19 +180,12 @@ class UdfpsController implements DozeReceiver {
mFgExecutor = fgExecutor;
mLayoutParams = createLayoutParams(context);
- int udfpsSensorId = -1;
- for (FingerprintSensorPropertiesInternal props :
- mFingerprintManager.getSensorPropertiesInternal()) {
- if (props.isAnyUdfpsType()) {
- udfpsSensorId = props.sensorId;
- break;
- }
- }
+ mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
- checkArgument(udfpsSensorId != -1);
- mUdfpsSensorId = udfpsSensorId;
+ checkArgument(mSensorProps != null);
mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
+ mView.setSensorProperties(mSensorProps);
mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path);
mHbmEnableCommand = resources.getString(R.string.udfps_hbm_enable_command);
@@ -235,6 +228,17 @@ class UdfpsController implements DozeReceiver {
mIsOverlayShowing = false;
}
+ @Nullable
+ private FingerprintSensorPropertiesInternal findFirstUdfps() {
+ for (FingerprintSensorPropertiesInternal props :
+ mFingerprintManager.getSensorPropertiesInternal()) {
+ if (props.isAnyUdfpsType()) {
+ return props;
+ }
+ }
+ return null;
+ }
+
@Override
public void dozeTimeTick() {
mView.dozeTimeTick();
@@ -374,7 +378,7 @@ class UdfpsController implements DozeReceiver {
fw.write(mHbmEnableCommand);
fw.close();
}
- mFingerprintManager.onPointerDown(mUdfpsSensorId, x, y, minor, major);
+ mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major);
} catch (IOException e) {
mView.hideScrimAndDot();
Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
@@ -382,7 +386,7 @@ class UdfpsController implements DozeReceiver {
}
private void onFingerUp() {
- mFingerprintManager.onPointerUp(mUdfpsSensorId);
+ mFingerprintManager.onPointerUp(mSensorProps.sensorId);
// Hiding the scrim before disabling HBM results in less noticeable flicker.
mView.hideScrimAndDot();
if (mHbmSupported) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index d7e91384f049..0ed3bda40c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -18,6 +18,7 @@ package com.android.systemui.biometrics;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -25,6 +26,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -55,8 +57,6 @@ public class UdfpsView extends View implements DozeReceiver,
private final RectF mSensorRect;
private final Paint mSensorPaint;
- private final float mSensorRadius;
- private final float mSensorCenterY;
private final float mSensorTouchAreaCoefficient;
private final int mMaxBurnInOffsetX;
private final int mMaxBurnInOffsetY;
@@ -64,9 +64,7 @@ public class UdfpsView extends View implements DozeReceiver,
private final Rect mTouchableRegion;
private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener;
- // This is calculated from the screen's dimensions at runtime, as opposed to mSensorCenterY,
- // which is defined in layout.xml
- private float mSensorCenterX;
+ @NonNull private FingerprintSensorPropertiesInternal mProps;
// AOD anti-burn-in offsets
private float mInterpolatedDarkAmount;
@@ -83,18 +81,10 @@ public class UdfpsView extends View implements DozeReceiver,
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0,
0);
try {
- if (!a.hasValue(R.styleable.UdfpsView_sensorRadius)) {
- throw new IllegalArgumentException("UdfpsView must contain sensorRadius");
- }
- if (!a.hasValue(R.styleable.UdfpsView_sensorCenterY)) {
- throw new IllegalArgumentException("UdfpsView must contain sensorMarginBottom");
- }
if (!a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) {
throw new IllegalArgumentException(
"UdfpsView must contain sensorTouchAreaCoefficient");
}
- mSensorRadius = a.getDimension(R.styleable.UdfpsView_sensorRadius, 0f);
- mSensorCenterY = a.getDimension(R.styleable.UdfpsView_sensorCenterY, 0f);
mSensorTouchAreaCoefficient = a.getFloat(
R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f);
} finally {
@@ -134,6 +124,10 @@ public class UdfpsView extends View implements DozeReceiver,
mIsScrimShowing = false;
}
+ void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
+ mProps = properties;
+ }
+
@Override
public void dozeTimeTick() {
updateAodPosition();
@@ -165,9 +159,10 @@ public class UdfpsView extends View implements DozeReceiver,
final int h = getLayoutParams().height;
final int w = getLayoutParams().width;
mScrimRect.set(0 /* left */, 0 /* top */, w, h);
- mSensorCenterX = w / 2f;
- mSensorRect.set(mSensorCenterX - mSensorRadius, mSensorCenterY - mSensorRadius,
- mSensorCenterX + mSensorRadius, mSensorCenterY + mSensorRadius);
+ mSensorRect.set(mProps.sensorLocationX - mProps.sensorRadius,
+ mProps.sensorLocationY - mProps.sensorRadius,
+ mProps.sensorLocationX + mProps.sensorRadius,
+ mProps.sensorLocationY + mProps.sensorRadius);
// Sets mTouchableRegion with rounded up values from mSensorRect.
mSensorRect.roundOut(mTouchableRegion);
@@ -211,10 +206,10 @@ public class UdfpsView extends View implements DozeReceiver,
}
boolean isValidTouch(float x, float y, float pressure) {
- return x > (mSensorCenterX - mSensorRadius * mSensorTouchAreaCoefficient)
- && x < (mSensorCenterX + mSensorRadius * mSensorTouchAreaCoefficient)
- && y > (mSensorCenterY - mSensorRadius * mSensorTouchAreaCoefficient)
- && y < (mSensorCenterY + mSensorRadius * mSensorTouchAreaCoefficient);
+ return x > (mProps.sensorLocationX - mProps.sensorRadius * mSensorTouchAreaCoefficient)
+ && x < (mProps.sensorLocationX + mProps.sensorRadius * mSensorTouchAreaCoefficient)
+ && y > (mProps.sensorLocationY - mProps.sensorRadius * mSensorTouchAreaCoefficient)
+ && y < (mProps.sensorLocationY + mProps.sensorRadius * mSensorTouchAreaCoefficient);
}
void setScrimAlpha(int alpha) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 1891daf59007..09d9e03cb0cb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -53,7 +53,8 @@ import java.util.Objects;
/**
* Encapsulates the data and UI elements of a bubble.
*/
-class Bubble implements BubbleViewProvider {
+@VisibleForTesting
+public class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
private final String mKey;
@@ -62,7 +63,7 @@ class Bubble implements BubbleViewProvider {
private long mLastAccessed;
@Nullable
- private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+ private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
/** Whether the bubble should show a dot for the notification indicating updated content. */
private boolean mShowBubbleUpdateDot = true;
@@ -173,8 +174,8 @@ class Bubble implements BubbleViewProvider {
@VisibleForTesting(visibility = PRIVATE)
Bubble(@NonNull final BubbleEntry entry,
- @Nullable final BubbleController.NotificationSuppressionChangedListener listener,
- final BubbleController.PendingIntentCanceledListener intentCancelListener) {
+ @Nullable final Bubbles.NotificationSuppressionChangedListener listener,
+ final Bubbles.PendingIntentCanceledListener intentCancelListener) {
mKey = entry.getKey();
mSuppressionListener = listener;
mIntentCancelListener = intent -> {
@@ -309,11 +310,13 @@ class Bubble implements BubbleViewProvider {
*
* @param callback the callback to notify one the bubble is ready to be displayed.
* @param context the context for the bubble.
+ * @param controller
* @param stackView the stackView the bubble is eventually added to.
* @param iconFactory the iconfactory use to create badged images for the bubble.
*/
void inflate(BubbleViewInfoTask.Callback callback,
Context context,
+ BubbleController controller,
BubbleStackView stackView,
BubbleIconFactory iconFactory,
boolean skipInflation) {
@@ -322,6 +325,7 @@ class Bubble implements BubbleViewProvider {
}
mInflationTask = new BubbleViewInfoTask(this,
context,
+ controller,
stackView,
iconFactory,
skipInflation,
@@ -403,7 +407,7 @@ class Bubble implements BubbleViewProvider {
mInstanceId = entry.getStatusBarNotification().getInstanceId();
mFlyoutMessage = extractFlyoutMessage(entry);
if (entry.getRanking() != null) {
- mShortcutInfo = entry.getRanking().getShortcutInfo();
+ mShortcutInfo = entry.getRanking().getConversationShortcutInfo();
mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
if (entry.getRanking().getChannel() != null) {
mIsImportantConversation =
@@ -522,7 +526,8 @@ class Bubble implements BubbleViewProvider {
/**
* Sets whether this notification should be suppressed in the shade.
*/
- void setSuppressNotification(boolean suppressNotification) {
+ @VisibleForTesting
+ public void setSuppressNotification(boolean suppressNotification) {
boolean prevShowInShade = showInShade();
if (suppressNotification) {
mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
@@ -559,7 +564,8 @@ class Bubble implements BubbleViewProvider {
/**
* Whether the flyout for the bubble should be shown.
*/
- boolean showFlyout() {
+ @VisibleForTesting
+ public boolean showFlyout() {
return !mSuppressFlyout && !mShouldSuppressPeek
&& !shouldSuppressNotification()
&& !mShouldSuppressNotificationList;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 0c3dc8222a34..598a604099ac 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -17,39 +17,19 @@
package com.android.systemui.bubbles;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.Notification.FLAG_BUBBLE;
-import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
-import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
-import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
-import static android.service.notification.NotificationListenerService.REASON_CLICK;
-import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
-import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
-import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
-import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.app.INotificationManager;
import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -65,7 +45,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -74,45 +53,14 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import androidx.annotation.IntDef;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dumpable;
import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationRemoveInterceptor;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.notification.NotificationChannelHelper;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -120,11 +68,10 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.function.IntConsumer;
/**
* Bubbles are a special type of content that can "float" on top of other apps or System UI.
@@ -132,52 +79,23 @@ import java.util.Objects;
*
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
-public class BubbleController implements Bubbles, ConfigurationController.ConfigurationListener,
- Dumpable {
+public class BubbleController implements Bubbles {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
- @Retention(SOURCE)
- @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
- DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
- DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
- DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
- DISMISS_NO_BUBBLE_UP})
- @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
- @interface DismissReason {}
-
- static final int DISMISS_USER_GESTURE = 1;
- static final int DISMISS_AGED = 2;
- static final int DISMISS_TASK_FINISHED = 3;
- static final int DISMISS_BLOCKED = 4;
- static final int DISMISS_NOTIF_CANCEL = 5;
- static final int DISMISS_ACCESSIBILITY_ACTION = 6;
- static final int DISMISS_NO_LONGER_BUBBLE = 7;
- static final int DISMISS_USER_CHANGED = 8;
- static final int DISMISS_GROUP_CANCELLED = 9;
- static final int DISMISS_INVALID_INTENT = 10;
- static final int DISMISS_OVERFLOW_MAX_REACHED = 11;
- static final int DISMISS_SHORTCUT_REMOVED = 12;
- static final int DISMISS_PACKAGE_REMOVED = 13;
- static final int DISMISS_NO_BUBBLE_UP = 14;
-
private final Context mContext;
- private final NotificationEntryManager mNotificationEntryManager;
- private final NotifPipeline mNotifPipeline;
- private final BubbleTaskStackListener mTaskStackListener;
private BubbleExpandListener mExpandListener;
@Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
- private final NotificationGroupManagerLegacy mNotificationGroupManager;
- private final ShadeController mShadeController;
private final FloatingContentCoordinator mFloatingContentCoordinator;
private final BubbleDataRepository mDataRepository;
private BubbleLogger mLogger;
private final Handler mMainHandler;
private BubbleData mBubbleData;
- private ScrimView mBubbleScrim;
+ private View mBubbleScrim;
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
private BubblePositioner mBubblePositioner;
+ private SysuiProxy mSysuiProxy;
/**
* The relative position of the stack when we removed it and nulled it out. If the stack is
@@ -193,12 +111,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
// Used when ranking updates occur and we check if things should bubble / unbubble
private NotificationListenerService.Ranking mTmpRanking;
- // Bubbles get added to the status bar view
- private final NotificationShadeWindowController mNotificationShadeWindowController;
- private final ZenModeController mZenModeController;
- private StatusBarStateListener mStatusBarStateListener;
- private INotificationManager mINotificationManager;
-
// Callback that updates BubbleOverflowActivity on data change.
@Nullable private BubbleData.Listener mOverflowListener = null;
@@ -209,12 +121,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
* When the shade status changes to SHADE (from anything but SHADE, like LOCKED) we'll select
* this bubble and expand the stack.
*/
- @Nullable private NotificationEntry mNotifEntryToExpandOnShadeUnlock;
+ @Nullable private BubbleEntry mNotifEntryToExpandOnShadeUnlock;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private IStatusBarService mBarService;
private WindowManager mWindowManager;
- private SysUiState mSysUiState;
// Used to post to main UI thread
private Handler mHandler = new Handler();
@@ -224,9 +134,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
/** Whether or not the BubbleStackView has been added to the WindowManager. */
private boolean mAddedToWindowManager = false;
- // Listens to user switch so bubbles can be saved and restored.
- private final NotificationLockscreenUserManager mNotifUserManager;
-
/** Last known orientation, used to detect orientation changes in {@link #onConfigChanged}. */
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -247,9 +154,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
private ShellTaskOrganizer mTaskOrganizer;
- // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
- private final List<NotifCallback> mCallbacks = new ArrayList<>();
-
/**
* Whether the IME is visible, as reported by the BubbleStackView. If it is, we'll make the
* Bubbles window NOT_FOCUSABLE so that touches on the Bubbles UI doesn't steal focus from the
@@ -257,122 +161,15 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
*/
private boolean mImeVisible = false;
- /**
- * Listener to find out about stack expansion / collapse events.
- */
- public interface BubbleExpandListener {
- /**
- * Called when the expansion state of the bubble stack changes.
- *
- * @param isExpanding whether it's expanding or collapsing
- * @param key the notification key associated with bubble being expanded
- */
- void onBubbleExpandChanged(boolean isExpanding, String key);
- }
-
- /**
- * Listener to be notified when a bubbles' notification suppression state changes.
- */
- public interface NotificationSuppressionChangedListener {
- /**
- * Called when the notification suppression state of a bubble changes.
- */
- void onBubbleNotificationSuppressionChange(Bubble bubble);
- }
-
- /**
- * Listener to be notified when a pending intent has been canceled for a bubble.
- */
- public interface PendingIntentCanceledListener {
- /**
- * Called when the pending intent for a bubble has been canceled.
- */
- void onPendingIntentCanceled(Bubble bubble);
- }
-
- /**
- * Callback for when the BubbleController wants to interact with the notification pipeline to:
- * - Remove a previously bubbled notification
- * - Update the notification shade since bubbled notification should/shouldn't be showing
- */
- public interface NotifCallback {
- /**
- * Called when a bubbled notification that was hidden from the shade is now being removed
- * This can happen when an app cancels a bubbled notification or when the user dismisses a
- * bubble.
- */
- void removeNotification(
- @NonNull NotificationEntry entry,
- @NonNull DismissedByUserStats stats,
- int reason);
-
- /**
- * Called when a bubbled notification has changed whether it should be
- * filtered from the shade.
- */
- void invalidateNotifications(@NonNull String reason);
-
- /**
- * Called on a bubbled entry that has been removed when there are no longer
- * bubbled entries in its group.
- *
- * Checks whether its group has any other (non-bubbled) children. If it doesn't,
- * removes all remnants of the group's summary from the notification pipeline.
- * TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
- */
- void maybeCancelSummary(@NonNull NotificationEntry entry);
- }
-
- /**
- * Listens for the current state of the status bar and updates the visibility state
- * of bubbles as needed.
- */
- private class StatusBarStateListener implements StatusBarStateController.StateListener {
- private int mState;
- /**
- * Returns the current status bar state.
- */
- public int getCurrentState() {
- return mState;
- }
-
- @Override
- public void onStateChanged(int newState) {
- mState = newState;
- boolean shouldCollapse = (mState != SHADE);
- if (shouldCollapse) {
- collapseStack();
- }
-
- if (mNotifEntryToExpandOnShadeUnlock != null) {
- expandStackAndSelectBubble(mNotifEntryToExpandOnShadeUnlock);
- mNotifEntryToExpandOnShadeUnlock = null;
- }
-
- updateStack();
- }
- }
+ /** true when user is in status bar unlock shade. */
+ private boolean mIsStatusBarShade = true;
/**
* Injected constructor. See {@link BubbleModule}.
*/
public static BubbleController create(Context context,
- NotificationShadeWindowController notificationShadeWindowController,
- StatusBarStateController statusBarStateController,
- ShadeController shadeController,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
- ConfigurationController configurationController,
- NotificationInterruptStateProvider interruptionStateProvider,
- ZenModeController zenModeController,
- NotificationLockscreenUserManager notifUserManager,
- NotificationGroupManagerLegacy groupManager,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- FeatureFlags featureFlags,
- DumpManager dumpManager,
FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState,
- INotificationManager notificationManager,
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -381,39 +178,23 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
@Main Handler mainHandler,
ShellTaskOrganizer organizer) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
- return new BubbleController(context, notificationShadeWindowController,
- statusBarStateController, shadeController, new BubbleData(context, logger),
- synchronizer, configurationController, interruptionStateProvider, zenModeController,
- notifUserManager, groupManager, entryManager, notifPipeline, featureFlags,
- dumpManager, floatingContentCoordinator,
- new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager,
- statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger,
- mainHandler, organizer, new BubblePositioner(context, windowManager));
+ return new BubbleController(context,
+ new BubbleData(context, logger), synchronizer,
+ floatingContentCoordinator, new BubbleDataRepository(context, launcherApps),
+ statusBarService, windowManager,
+ windowManagerShellWrapper, launcherApps, logger, mainHandler, organizer,
+ new BubblePositioner(context, windowManager));
}
/**
* Testing constructor.
*/
@VisibleForTesting
- BubbleController(Context context,
- NotificationShadeWindowController notificationShadeWindowController,
- StatusBarStateController statusBarStateController,
- ShadeController shadeController,
+ public BubbleController(Context context,
BubbleData data,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
- ConfigurationController configurationController,
- NotificationInterruptStateProvider interruptionStateProvider,
- ZenModeController zenModeController,
- NotificationLockscreenUserManager notifUserManager,
- NotificationGroupManagerLegacy groupManager,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- FeatureFlags featureFlags,
- DumpManager dumpManager,
FloatingContentCoordinator floatingContentCoordinator,
BubbleDataRepository dataRepository,
- SysUiState sysUiState,
- INotificationManager notificationManager,
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -422,82 +203,35 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
Handler mainHandler,
ShellTaskOrganizer organizer,
BubblePositioner positioner) {
- dumpManager.registerDumpable(TAG, this);
mContext = context;
- mShadeController = shadeController;
- mNotificationInterruptStateProvider = interruptionStateProvider;
- mNotifUserManager = notifUserManager;
- mZenModeController = zenModeController;
mFloatingContentCoordinator = floatingContentCoordinator;
mDataRepository = dataRepository;
- mINotificationManager = notificationManager;
mLogger = bubbleLogger;
mMainHandler = mainHandler;
- mZenModeController.addCallback(new ZenModeController.Callback() {
- @Override
- public void onZenChanged(int zen) {
- for (Bubble b : mBubbleData.getBubbles()) {
- b.setShowDot(b.showInShade());
- }
- }
-
- @Override
- public void onConfigChanged(ZenModeConfig config) {
- for (Bubble b : mBubbleData.getBubbles()) {
- b.setShowDot(b.showInShade());
- }
- }
- });
-
- configurationController.addCallback(this /* configurationListener */);
- mSysUiState = sysUiState;
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
- mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
- @Override
- public void onBubbleNotificationSuppressionChange(Bubble bubble) {
- // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it
- // can tell.
- try {
- mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
- !bubble.showInShade());
- } catch (RemoteException e) {
- // Bad things have happened
- }
+ mBubbleData.setSuppressionChangedListener(bubble -> {
+ // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it
+ // can tell.
+ try {
+ mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
+ !bubble.showInShade());
+ } catch (RemoteException e) {
+ // Bad things have happened
}
});
mBubbleData.setPendingIntentCancelledListener(bubble -> {
if (bubble.getBubbleIntent() == null) {
return;
}
- if (bubble.isIntentActive()
- || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
+ if (bubble.isIntentActive() || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
bubble.setPendingIntentCanceled();
return;
}
- mHandler.post(
- () -> removeBubble(bubble.getKey(),
- BubbleController.DISMISS_INVALID_INTENT));
+ mHandler.post(() -> removeBubble(bubble.getKey(), DISMISS_INVALID_INTENT));
});
- mNotificationEntryManager = entryManager;
- mNotificationGroupManager = groupManager;
- mNotifPipeline = notifPipeline;
-
- if (!featureFlags.isNewNotifPipelineRenderingEnabled()) {
- setupNEM();
- } else {
- setupNotifPipeline();
- }
-
- mNotificationShadeWindowController = notificationShadeWindowController;
- mStatusBarStateListener = new StatusBarStateListener();
- statusBarStateController.addCallback(mStatusBarStateListener);
-
- mTaskStackListener = new BubbleTaskStackListener();
- TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
-
try {
windowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener());
} catch (RemoteException e) {
@@ -511,25 +245,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
ServiceManager.getService(Context.STATUS_BAR_SERVICE))
: statusBarService;
- mBubbleScrim = new ScrimView(mContext);
- mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-
mSavedBubbleKeysPerUser = new SparseSetArray<>();
- mCurrentUserId = mNotifUserManager.getCurrentUserId();
+ mCurrentUserId = ActivityManager.getCurrentUser();
mBubbleData.setCurrentUserId(mCurrentUserId);
- mNotifUserManager.addUserChangedListener(
- new NotificationLockscreenUserManager.UserChangedListener() {
- @Override
- public void onUserChanged(int newUserId) {
- BubbleController.this.saveBubbles(mCurrentUserId);
- mBubbleData.dismissAll(DISMISS_USER_CHANGED);
- BubbleController.this.restoreBubbles(newUserId);
- mCurrentUserId = newUserId;
- mBubbleData.setCurrentUserId(newUserId);
- }
- });
-
mBubbleIconFactory = new BubbleIconFactory(context);
mTaskOrganizer = organizer;
mBubblePositioner = positioner;
@@ -549,10 +268,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
@Override
- public void onPackagesAvailable(String[] strings, UserHandle userHandle,
- boolean b) {
-
- }
+ public void onPackagesAvailable(String[] strings, UserHandle userHandle, boolean b) {}
@Override
public void onPackagesUnavailable(String[] packages, UserHandle userHandle,
@@ -577,17 +293,9 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
/**
- * See {@link NotifCallback}.
- */
- @Override
- public void addNotifCallback(NotifCallback callback) {
- mCallbacks.add(callback);
- }
-
- /**
* Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
*/
- public void hideCurrentInputMethod() {
+ void hideCurrentInputMethod() {
try {
mBarService.hideCurrentInputMethodForBubbles();
} catch (RemoteException e) {
@@ -595,183 +303,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
- private void onBubbleExpandChanged(boolean shouldExpand) {
- mSysUiState
- .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
- .commitUpdate(mContext.getDisplayId());
- }
-
- private void setupNEM() {
- mNotificationEntryManager.addNotificationEntryListener(
- new NotificationEntryListener() {
- @Override
- public void onPendingEntryAdded(NotificationEntry entry) {
- onEntryAdded(entry);
- }
-
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- onEntryUpdated(entry);
- }
-
- @Override
- public void onEntryRemoved(
- NotificationEntry entry,
- @android.annotation.Nullable NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- BubbleController.this.onEntryRemoved(entry);
- }
-
- @Override
- public void onNotificationRankingUpdated(RankingMap rankingMap) {
- onRankingUpdated(rankingMap);
- }
- });
-
- // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
- mNotificationEntryManager.addNotificationRemoveInterceptor(
- new NotificationRemoveInterceptor() {
- @Override
- public boolean onNotificationRemoveRequested(
- String key,
- NotificationEntry entry,
- int dismissReason) {
- final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
- final boolean isUserDismiss = dismissReason == REASON_CANCEL
- || dismissReason == REASON_CLICK;
- final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
- || dismissReason == REASON_APP_CANCEL_ALL;
- final boolean isSummaryCancel =
- dismissReason == REASON_GROUP_SUMMARY_CANCELED;
-
- // Need to check for !appCancel here because the notification may have
- // previously been dismissed & entry.isRowDismissed would still be true
- boolean userRemovedNotif =
- (entry != null && entry.isRowDismissed() && !isAppCancel)
- || isClearAll || isUserDismiss || isSummaryCancel;
-
- if (userRemovedNotif) {
- return handleDismissalInterception(entry);
- }
- return false;
- }
- });
-
- mNotificationGroupManager.registerGroupChangeListener(
- new NotificationGroupManagerLegacy.OnGroupChangeListener() {
- @Override
- public void onGroupSuppressionChanged(
- NotificationGroupManagerLegacy.NotificationGroup group,
- boolean suppressed) {
- // More notifications could be added causing summary to no longer
- // be suppressed -- in this case need to remove the key.
- final String groupKey = group.summary != null
- ? group.summary.getSbn().getGroupKey()
- : null;
- if (!suppressed && groupKey != null
- && mBubbleData.isSummarySuppressed(groupKey)) {
- mBubbleData.removeSuppressedSummary(groupKey);
- }
- }
- });
-
- addNotifCallback(new NotifCallback() {
- @Override
- public void removeNotification(
- NotificationEntry entry,
- DismissedByUserStats dismissedByUserStats,
- int reason
- ) {
- mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
- dismissedByUserStats, reason);
- }
-
- @Override
- public void invalidateNotifications(String reason) {
- mNotificationEntryManager.updateNotifications(reason);
- }
-
- @Override
- public void maybeCancelSummary(NotificationEntry entry) {
- // Check if removed bubble has an associated suppressed group summary that needs
- // to be removed now.
- final String groupKey = entry.getSbn().getGroupKey();
- if (mBubbleData.isSummarySuppressed(groupKey)) {
- mBubbleData.removeSuppressedSummary(groupKey);
-
- final NotificationEntry summary =
- mNotificationEntryManager.getActiveNotificationUnfiltered(
- mBubbleData.getSummaryKey(groupKey));
- if (summary != null) {
- mNotificationEntryManager.performRemoveNotification(
- summary.getSbn(),
- getDismissedByUserStats(summary, false),
- UNDEFINED_DISMISS_REASON);
- }
- }
-
- // Check if we still need to remove the summary from NoManGroup because the summary
- // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
- // For example:
- // 1. Bubbled notifications (group) is posted to shade and are visible bubbles
- // 2. User expands bubbles so now their respective notifications in the shade are
- // hidden, including the group summary
- // 3. User removes all bubbles
- // 4. We expect all the removed bubbles AND the summary (note: the summary was
- // never added to the suppressedSummary list in BubbleData, so we add this check)
- NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry);
- if (summary != null) {
- ArrayList<NotificationEntry> summaryChildren =
- mNotificationGroupManager.getLogicalChildren(summary.getSbn());
- boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
- if (!isSummaryThisNotif && (summaryChildren == null
- || summaryChildren.isEmpty())) {
- mNotificationEntryManager.performRemoveNotification(
- summary.getSbn(),
- getDismissedByUserStats(summary, false),
- UNDEFINED_DISMISS_REASON);
- }
- }
- }
- });
- }
-
- private void setupNotifPipeline() {
- mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
- @Override
- public void onEntryAdded(NotificationEntry entry) {
- BubbleController.this.onEntryAdded(entry);
- }
-
- @Override
- public void onEntryUpdated(NotificationEntry entry) {
- BubbleController.this.onEntryUpdated(entry);
- }
-
- @Override
- public void onRankingUpdate(RankingMap rankingMap) {
- onRankingUpdated(rankingMap);
- }
-
- @Override
- public void onEntryRemoved(NotificationEntry entry,
- @NotifCollection.CancellationReason int reason) {
- BubbleController.this.onEntryRemoved(entry);
- }
- });
- }
-
- /**
- * Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController}
- * since we want the scrim's appearance and behavior to be identical to that of the notification
- * shade scrim.
- */
- @Override
- public ScrimView getScrimForBubble() {
- return mBubbleScrim;
- }
-
/**
* Called when the status bar has become visible or invisible (either permanently or
* temporarily).
@@ -785,16 +316,47 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
+ @Override
+ public void onZenStateChanged() {
+ for (Bubble b : mBubbleData.getBubbles()) {
+ b.setShowDot(b.showInShade());
+ }
+ }
+
+ @Override
+ public void onStatusBarStateChanged(boolean isShade) {
+ mIsStatusBarShade = isShade;
+ if (!mIsStatusBarShade) {
+ collapseStack();
+ }
+
+ if (mNotifEntryToExpandOnShadeUnlock != null) {
+ expandStackAndSelectBubble(mNotifEntryToExpandOnShadeUnlock);
+ mNotifEntryToExpandOnShadeUnlock = null;
+ }
+
+ updateStack();
+ }
+
+ @Override
+ public void onUserChanged(int newUserId) {
+ saveBubbles(mCurrentUserId);
+ mBubbleData.dismissAll(DISMISS_USER_CHANGED);
+ restoreBubbles(newUserId);
+ mCurrentUserId = newUserId;
+ mBubbleData.setCurrentUserId(newUserId);
+ }
+
/**
* Sets whether to perform inflation on the same thread as the caller. This method should only
* be used in tests, not in production.
*/
@VisibleForTesting
- void setInflateSynchronously(boolean inflateSynchronously) {
+ public void setInflateSynchronously(boolean inflateSynchronously) {
mInflateSynchronously = inflateSynchronously;
}
- @Override
+ /** Set a listener to be notified of when overflow view update. */
public void setOverflowListener(BubbleData.Listener listener) {
mOverflowListener = listener;
}
@@ -802,21 +364,24 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
/**
* @return Bubbles for updating overflow.
*/
- @Override
- public List<Bubble> getOverflowBubbles() {
+ List<Bubble> getOverflowBubbles() {
return mBubbleData.getOverflowBubbles();
}
- @Override
+ /** The task listener for events in bubble tasks. */
public ShellTaskOrganizer getTaskOrganizer() {
return mTaskOrganizer;
}
- @Override
- public BubblePositioner getPositioner() {
+ /** Contains information to help position things on the screen. */
+ BubblePositioner getPositioner() {
return mBubblePositioner;
}
+ SysuiProxy getSysuiProxy() {
+ return mSysuiProxy;
+ }
+
/**
* BubbleStackView is lazily created by this method the first time a Bubble is added. This
* method initializes the stack view and adds it to the StatusBar just above the scrim.
@@ -824,23 +389,14 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
private void ensureStackViewCreated() {
if (mStackView == null) {
mStackView = new BubbleStackView(
- mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
- this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged,
- this::hideCurrentInputMethod, this::onBubbleExpandChanged, mBubblePositioner);
+ mContext, this, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
mStackView.setStackStartPosition(mPositionFromRemovedStack);
mStackView.addView(mBubbleScrim);
mStackView.onOrientationChanged();
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
}
-
- mStackView.setUnbubbleConversationCallback(key -> {
- final NotificationEntry entry =
- mNotificationEntryManager.getPendingOrActiveNotif(key);
- if (entry != null) {
- onUserChangedBubble(entry, false /* shouldBubble */);
- }
- });
+ mStackView.setUnbubbleConversationCallback(mSysuiProxy::onUnbubbleConversation);
}
addToWindowManagerMaybe();
@@ -882,7 +438,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
- private void onImeVisibilityChanged(boolean imeVisible) {
+ void onImeVisibilityChanged(boolean imeVisible) {
mImeVisible = imeVisible;
}
@@ -913,7 +469,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
* Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
* added in the meantime.
*/
- private void onAllBubblesAnimatedOut() {
+ void onAllBubblesAnimatedOut() {
if (mStackView != null) {
mStackView.setVisibility(INVISIBLE);
removeFromWindowManagerMaybe();
@@ -946,12 +502,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
// There were no bubbles saved for this used.
return;
}
- for (NotificationEntry e :
- mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
- if (savedBubbleKeys.contains(e.getKey())
- && mNotificationInterruptStateProvider.shouldBubbleUp(e)
- && e.isBubble()
- && canLaunchInActivityView(mContext, e)) {
+ for (BubbleEntry e : mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys)) {
+ if (canLaunchInActivityView(mContext, e)) {
updateBubble(e, true /* suppressFlyout */, false /* showInShade */);
}
}
@@ -960,27 +512,18 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
@Override
- public void onUiModeChanged() {
- updateForThemeChanges();
- }
-
- @Override
- public void onOverlayChanged() {
- updateForThemeChanges();
- }
-
- private void updateForThemeChanges() {
+ public void updateForThemeChanges() {
if (mStackView != null) {
mStackView.onThemeChanged();
}
mBubbleIconFactory = new BubbleIconFactory(mContext);
// Reload each bubble
for (Bubble b: mBubbleData.getBubbles()) {
- b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory,
+ b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory,
false /* skipInflation */);
}
for (Bubble b: mBubbleData.getOverflowBubbles()) {
- b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory,
+ b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory,
false /* skipInflation */);
}
}
@@ -1012,9 +555,16 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
- /**
- * Set a listener to be notified of bubble expand events.
- */
+ @Override
+ public void setBubbleScrim(View view) {
+ mBubbleScrim = view;
+ }
+
+ @Override
+ public void setSysuiProxy(SysuiProxy proxy) {
+ mSysuiProxy = proxy;
+ }
+
@Override
public void setExpandListener(BubbleExpandListener listener) {
mExpandListener = ((isExpanding, key) -> {
@@ -1032,7 +582,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
* screen (e.g. if on AOD).
*/
@VisibleForTesting
- boolean hasBubbles() {
+ public boolean hasBubbles() {
if (mStackView == null) {
return false;
}
@@ -1050,24 +600,37 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
@Override
- public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) {
- String key = entry.getKey();
+ public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
&& !mBubbleData.getAnyBubbleWithkey(key).showInShade());
- String groupKey = entry.getSbn().getGroupKey();
boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey));
return (isSummary && isSuppressedSummary) || isSuppressedBubble;
}
@Override
- public boolean isBubbleExpanded(NotificationEntry entry) {
- return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
- && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey());
+ public boolean isSummarySuppressed(String groupKey) {
+ return mBubbleData.isSummarySuppressed(groupKey);
}
@Override
+ public void removeSuppressedSummary(String groupKey) {
+ mBubbleData.removeSuppressedSummary(groupKey);
+ }
+
+ @Override
+ public String getSummaryKey(String groupKey) {
+ return mBubbleData.getSummaryKey(groupKey);
+ }
+
+ @Override
+ public boolean isBubbleExpanded(String key) {
+ return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
+ && mBubbleData.getSelectedBubble().getKey().equals(key);
+ }
+
+ /** Promote the provided bubble from the overflow view. */
public void promoteBubbleFromOverflow(Bubble bubble) {
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
bubble.setInflateSynchronously(mInflateSynchronously);
@@ -1077,8 +640,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
@Override
- public void expandStackAndSelectBubble(NotificationEntry entry) {
- if (mStatusBarStateListener.getCurrentState() == SHADE) {
+ public void expandStackAndSelectBubble(BubbleEntry entry) {
+ if (mIsStatusBarShade) {
mNotifEntryToExpandOnShadeUnlock = null;
String key = entry.getKey();
@@ -1104,27 +667,13 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
- @Override
- public void onUserChangedImportance(NotificationEntry entry) {
- try {
- int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
- flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
- mBarService.onNotificationBubbleChanged(entry.getKey(), true, flags);
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
- }
- mShadeController.collapsePanel(true);
- if (entry.getRow() != null) {
- entry.getRow().updateBubbleButton();
- }
- }
-
/**
* Adds or updates a bubble associated with the provided notification entry.
*
* @param notif the notification associated with this bubble.
*/
- void updateBubble(NotificationEntry notif) {
+ @VisibleForTesting
+ public void updateBubble(BubbleEntry notif) {
updateBubble(notif, false /* suppressFlyout */, true /* showInShade */);
}
@@ -1144,27 +693,32 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
return;
}
bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble),
- mContext, mStackView, mBubbleIconFactory, true /* skipInflation */);
+ mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */);
});
return null;
});
}
- void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) {
+ /**
+ * Adds or updates a bubble associated with the provided notification entry.
+ *
+ * @param notif the notification associated with this bubble.
+ * @param suppressFlyout this bubble suppress flyout or not.
+ * @param showInShade this bubble show in shade or not.
+ */
+ @VisibleForTesting
+ public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) {
// If this is an interruptive notif, mark that it's interrupted
- if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
- notif.setInterruption();
- }
+ mSysuiProxy.setNotificationInterruption(notif.getKey());
if (!notif.getRanking().visuallyInterruptive()
&& (notif.getBubbleMetadata() != null
&& !notif.getBubbleMetadata().getAutoExpandBubble())
&& mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
// Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
- b.setEntry(notifToBubbleEntry(notif));
+ b.setEntry(notif);
} else {
- Bubble bubble = mBubbleData.getOrCreateBubble(
- notifToBubbleEntry(notif), null /* persistedBubble */);
+ Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
inflateAndAdd(bubble, suppressFlyout, showInShade);
}
}
@@ -1174,68 +728,33 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
ensureStackViewCreated();
bubble.setInflateSynchronously(mInflateSynchronously);
bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
- mContext, mStackView, mBubbleIconFactory, false /* skipInflation */);
- }
-
- @Override
- public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) {
- NotificationChannel channel = entry.getChannel();
- final String appPkg = entry.getSbn().getPackageName();
- final int appUid = entry.getSbn().getUid();
- if (channel == null || appPkg == null) {
- return;
- }
-
- // Update the state in NotificationManagerService
- try {
- int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
- flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
- mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags);
- } catch (RemoteException e) {
- }
-
- // Change the settings
- channel = NotificationChannelHelper.createConversationChannelIfNeeded(mContext,
- mINotificationManager, entry, channel);
- channel.setAllowBubbles(shouldBubble);
- try {
- int currentPref = mINotificationManager.getBubblePreferenceForPackage(appPkg, appUid);
- if (shouldBubble && currentPref == BUBBLE_PREFERENCE_NONE) {
- mINotificationManager.setBubblesAllowed(appPkg, appUid, BUBBLE_PREFERENCE_SELECTED);
- }
- mINotificationManager.updateNotificationChannelForPackage(appPkg, appUid, channel);
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
- }
-
- if (shouldBubble) {
- mShadeController.collapsePanel(true);
- if (entry.getRow() != null) {
- entry.getRow().updateBubbleButton();
- }
- }
+ mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */);
}
+ /**
+ * Removes the bubble with the given key.
+ * <p>
+ * Must be called from the main thread.
+ */
+ @VisibleForTesting
@MainThread
- @Override
public void removeBubble(String key, int reason) {
if (mBubbleData.hasAnyBubbleWithKey(key)) {
mBubbleData.dismissBubbleWithKey(key, reason);
}
}
- private void onEntryAdded(NotificationEntry entry) {
- if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- && entry.isBubble()
- && canLaunchInActivityView(mContext, entry)) {
+ @Override
+ public void onEntryAdded(BubbleEntry entry) {
+ if (canLaunchInActivityView(mContext, entry)) {
updateBubble(entry);
}
}
- private void onEntryUpdated(NotificationEntry entry) {
+ @Override
+ public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
// shouldBubbleUp checks canBubble & for bubble metadata
- boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- && canLaunchInActivityView(mContext, entry);
+ boolean shouldBubble = shouldBubbleUp && canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
@@ -1244,9 +763,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
- private void onEntryRemoved(NotificationEntry entry) {
+ @Override
+ public void onEntryRemoved(BubbleEntry entry) {
if (isSummaryOfBubbles(entry)) {
- final String groupKey = entry.getSbn().getGroupKey();
+ final String groupKey = entry.getStatusBarNotification().getGroupKey();
mBubbleData.removeSuppressedSummary(groupKey);
// Remove any associated bubble children with the summary
@@ -1259,39 +779,30 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
- /**
- * Called when NotificationListener has received adjusted notification rank and reapplied
- * filtering and sorting. This is used to dismiss or create bubbles based on changes in
- * permissions on the notification channel or the global setting.
- *
- * @param rankingMap the updated ranking map from NotificationListenerService
- */
- private void onRankingUpdated(RankingMap rankingMap) {
+ @Override
+ public void onRankingUpdated(RankingMap rankingMap) {
if (mTmpRanking == null) {
mTmpRanking = new NotificationListenerService.Ranking();
}
String[] orderedKeys = rankingMap.getOrderedKeys();
for (int i = 0; i < orderedKeys.length; i++) {
String key = orderedKeys[i];
- NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
+ BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(key);
rankingMap.getRanking(key, mTmpRanking);
boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
if (isActiveBubble && !mTmpRanking.canBubble()) {
// If this entry is no longer allowed to bubble, dismiss with the BLOCKED reason.
// This means that the app or channel's ability to bubble has been revoked.
- mBubbleData.dismissBubbleWithKey(
- key, BubbleController.DISMISS_BLOCKED);
- } else if (isActiveBubble
- && !mNotificationInterruptStateProvider.shouldBubbleUp(entry)) {
+ mBubbleData.dismissBubbleWithKey(key, DISMISS_BLOCKED);
+ } else if (isActiveBubble && !mSysuiProxy.shouldBubbleUp(key)) {
// If this entry is allowed to bubble, but cannot currently bubble up, dismiss it.
// This happens when DND is enabled and configured to hide bubbles. Dismissing with
// the reason DISMISS_NO_BUBBLE_UP will retain the underlying notification, so that
// the bubble will be re-created if shouldBubbleUp returns true.
- mBubbleData.dismissBubbleWithKey(
- key, BubbleController.DISMISS_NO_BUBBLE_UP);
+ mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
} else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
entry.setFlagBubble(true);
- onEntryUpdated(entry);
+ onEntryUpdated(entry, true /* shouldBubbleUp */);
}
}
}
@@ -1306,23 +817,18 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
return bubbleChildren;
}
for (Bubble bubble : mBubbleData.getActiveBubbles()) {
- final NotificationEntry entry =
- mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey());
- if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
+ final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey());
+ if (entry != null && groupKey.equals(entry.getStatusBarNotification().getGroupKey())) {
bubbleChildren.add(bubble);
}
}
return bubbleChildren;
}
- private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble,
+ private void setIsBubble(@NonNull final BubbleEntry entry, final boolean isBubble,
final boolean autoExpand) {
Objects.requireNonNull(entry);
- if (isBubble) {
- entry.getSbn().getNotification().flags |= FLAG_BUBBLE;
- } else {
- entry.getSbn().getNotification().flags &= ~FLAG_BUBBLE;
- }
+ entry.setFlagBubble(isBubble);
try {
int flags = 0;
if (autoExpand) {
@@ -1338,8 +844,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) {
Objects.requireNonNull(b);
b.setIsBubble(isBubble);
- final NotificationEntry entry = mNotificationEntryManager
- .getPendingOrActiveNotif(b.getKey());
+ final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(b.getKey());
if (entry != null) {
// Updating the entry to be a bubble will trigger our normal update flow
setIsBubble(entry, isBubble, b.shouldAutoExpand());
@@ -1372,7 +877,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
// Collapsing? Do this first before remaining steps.
if (update.expandedChanged && !update.expanded) {
mStackView.setExpanded(false);
- mNotificationShadeWindowController.setRequestTopUi(false, TAG);
+ mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
}
// Do removals, if any.
@@ -1396,8 +901,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
if (reason == DISMISS_NOTIF_CANCEL) {
bubblesToBeRemovedFromRepository.add(bubble);
}
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- bubble.getKey());
if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey())
&& (!bubble.showInShade()
@@ -1405,31 +908,21 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
|| reason == DISMISS_GROUP_CANCELLED)) {
// The bubble is now gone & the notification is hidden from the shade, so
// time to actually remove it
- for (NotifCallback cb : mCallbacks) {
- if (entry != null) {
- cb.removeNotification(
- entry,
- getDismissedByUserStats(entry, true),
- REASON_CANCEL);
- }
- }
+ mSysuiProxy.notifyRemoveNotification(bubble.getKey(), REASON_CANCEL);
} else {
if (bubble.isBubble()) {
setIsBubble(bubble, false /* isBubble */);
}
- if (entry != null && entry.getRow() != null) {
- entry.getRow().updateBubbleButton();
- }
+ mSysuiProxy.updateNotificationBubbleButton(bubble.getKey());
}
}
+ final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey());
if (entry != null) {
- final String groupKey = entry.getSbn().getGroupKey();
+ final String groupKey = entry.getStatusBarNotification().getGroupKey();
if (getBubblesInGroup(groupKey).isEmpty()) {
// Time to potentially remove the summary
- for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(entry);
- }
+ mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey());
}
}
}
@@ -1454,11 +947,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
if (update.selectionChanged && mStackView != null) {
mStackView.setSelectedBubble(update.selectedBubble);
if (update.selectedBubble != null) {
- final NotificationEntry entry = mNotificationEntryManager
- .getPendingOrActiveNotif(update.selectedBubble.getKey());
- if (entry != null) {
- mNotificationGroupManager.updateSuppression(entry);
- }
+ mSysuiProxy.updateNotificationSuppression(update.selectedBubble.getKey());
}
}
@@ -1466,24 +955,20 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
if (update.expandedChanged && update.expanded) {
if (mStackView != null) {
mStackView.setExpanded(true);
- mNotificationShadeWindowController.setRequestTopUi(true, TAG);
+ mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
}
}
- for (NotifCallback cb : mCallbacks) {
- cb.invalidateNotifications("BubbleData.Listener.applyUpdate");
- }
+ mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate");
updateStack();
}
};
@Override
- public boolean handleDismissalInterception(NotificationEntry entry) {
- if (entry == null) {
- return false;
- }
+ public boolean handleDismissalInterception(BubbleEntry entry,
+ @Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
if (isSummaryOfBubbles(entry)) {
- handleSummaryDismissalInterception(entry);
+ handleSummaryDismissalInterception(entry, children, removeCallback);
} else {
Bubble bubble = mBubbleData.getBubbleInStackWithKey(entry.getKey());
if (bubble == null || !entry.isBubble()) {
@@ -1496,87 +981,50 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
bubble.setShowDot(false /* show */);
}
// Update the shade
- for (NotifCallback cb : mCallbacks) {
- cb.invalidateNotifications("BubbleController.handleDismissalInterception");
- }
+ mSysuiProxy.notifyInvalidateNotifications("BubbleController.handleDismissalInterception");
return true;
}
- private boolean isSummaryOfBubbles(NotificationEntry entry) {
- if (entry == null) {
- return false;
- }
-
- String groupKey = entry.getSbn().getGroupKey();
+ private boolean isSummaryOfBubbles(BubbleEntry entry) {
+ String groupKey = entry.getStatusBarNotification().getGroupKey();
ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
- boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
- return (isSuppressedSummary || isSummary)
- && bubbleChildren != null
- && !bubbleChildren.isEmpty();
+ boolean isSummary = entry.getStatusBarNotification().getNotification().isGroupSummary();
+ return (isSuppressedSummary || isSummary) && !bubbleChildren.isEmpty();
}
- private void handleSummaryDismissalInterception(NotificationEntry summary) {
- // current children in the row:
- final List<NotificationEntry> children = summary.getAttachedNotifChildren();
+ private void handleSummaryDismissalInterception(
+ BubbleEntry summary, @Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
if (children != null) {
for (int i = 0; i < children.size(); i++) {
- NotificationEntry child = children.get(i);
+ BubbleEntry child = children.get(i);
if (mBubbleData.hasAnyBubbleWithKey(child.getKey())) {
// Suppress the bubbled child
// As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed.
Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey());
if (bubbleChild != null) {
- final NotificationEntry entry = mNotificationEntryManager
- .getPendingOrActiveNotif(bubbleChild.getKey());
- if (entry != null) {
- mNotificationGroupManager.onEntryRemoved(entry);
- }
+ mSysuiProxy.removeNotificationEntry(bubbleChild.getKey());
bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */);
}
} else {
// non-bubbled children can be removed
- for (NotifCallback cb : mCallbacks) {
- cb.removeNotification(
- child,
- getDismissedByUserStats(child, true),
- REASON_GROUP_SUMMARY_CANCELED);
- }
+ removeCallback.accept(i);
}
}
}
// And since all children are removed, remove the summary.
- mNotificationGroupManager.onEntryRemoved(summary);
+ removeCallback.accept(-1);
// TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated
- mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
+ mBubbleData.addSummaryToSuppress(summary.getStatusBarNotification().getGroupKey(),
summary.getKey());
}
/**
- * Gets the DismissedByUserStats used by {@link NotificationEntryManager}.
- * Will not be necessary when using the new notification pipeline's {@link NotifCollection}.
- * Instead, this is taken care of by {@link BubbleCoordinator}.
- */
- private DismissedByUserStats getDismissedByUserStats(
- NotificationEntry entry,
- boolean isVisible) {
- return new DismissedByUserStats(
- DISMISSAL_BUBBLE,
- DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(
- entry.getKey(),
- entry.getRanking().getRank(),
- mNotificationEntryManager.getActiveNotificationsCount(),
- isVisible,
- NotificationLogger.getNotificationLocation(entry)));
- }
-
- /**
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides.
* Updates stack description for TalkBack focus.
@@ -1586,7 +1034,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
return;
}
- if (mStatusBarStateListener.getCurrentState() != SHADE) {
+ if (!mIsStatusBarShade) {
// Bubbles don't appear over the locked shade.
mStackView.setVisibility(INVISIBLE);
} else if (hasBubbles()) {
@@ -1610,20 +1058,21 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble();
if (expandedViewProvider != null && isStackExpanded()
&& !mStackView.isExpansionAnimating()
- && !mNotificationShadeWindowController.getPanelExpanded()) {
+ && !mSysuiProxy.isNotificationShadeExpand()) {
return expandedViewProvider.getTaskId();
}
return INVALID_TASK_ID;
}
@VisibleForTesting
- BubbleStackView getStackView() {
+ public BubbleStackView getStackView() {
return mStackView;
}
/**
* Description of current bubble state.
*/
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("BubbleController state:");
mBubbleData.dump(fd, pw, args);
@@ -1635,39 +1084,6 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
/**
- * This task stack listener is responsible for responding to tasks moved to the front
- * which are on the default (main) display. When this happens, expanded bubbles must be
- * collapsed so the user may interact with the app which was just moved to the front.
- * <p>
- * This listener is registered with SystemUI's ActivityManagerWrapper which dispatches
- * these calls via a main thread Handler.
- */
- @MainThread
- private class BubbleTaskStackListener extends TaskStackChangeListener {
-
- @Override
- public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
- int expandedId = getExpandedTaskId();
- if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) {
- mBubbleData.setExpanded(false);
- }
- }
-
- @Override
- public void onActivityRestartAttempt(RunningTaskInfo taskInfo, boolean homeTaskVisible,
- boolean clearedTask, boolean wasVisible) {
- for (Bubble b : mBubbleData.getBubbles()) {
- if (taskInfo.taskId == b.getTaskId()) {
- mBubbleData.setSelectedBubble(b);
- mBubbleData.setExpanded(true);
- return;
- }
- }
- }
-
- }
-
- /**
* Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
*
* Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically
@@ -1676,7 +1092,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
* @param context the context to use.
* @param entry the entry to bubble.
*/
- static boolean canLaunchInActivityView(Context context, NotificationEntry entry) {
+ static boolean canLaunchInActivityView(Context context, BubbleEntry entry) {
PendingIntent intent = entry.getBubbleMetadata() != null
? entry.getBubbleMetadata().getIntent()
: null;
@@ -1688,8 +1104,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey());
return false;
}
- PackageManager packageManager = StatusBar.getPackageManagerForUser(
- context, entry.getSbn().getUser().getIdentifier());
+ PackageManager packageManager = getPackageManagerForUser(
+ context, entry.getStatusBarNotification().getUser().getIdentifier());
ActivityInfo info =
intent.getIntent().resolveActivityInfo(packageManager, 0);
if (info == null) {
@@ -1707,6 +1123,24 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
return true;
}
+ static PackageManager getPackageManagerForUser(Context context, int userId) {
+ Context contextForUser = context;
+ // UserHandle defines special userId as negative values, e.g. USER_ALL
+ if (userId >= 0) {
+ try {
+ // Create a context for the correct user so if a package isn't installed
+ // for user 0 we can still load information about the package.
+ contextForUser =
+ context.createPackageContextAsUser(context.getPackageName(),
+ Context.CONTEXT_RESTRICTED,
+ new UserHandle(userId));
+ } catch (PackageManager.NameNotFoundException e) {
+ // Shouldn't fail to find the package name for system ui.
+ }
+ }
+ return contextForUser.getPackageManager();
+ }
+
/** PinnedStackListener that dispatches IME visibility updates to the stack. */
//TODO(b/170442945): Better way to do this / insets listener?
private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener {
@@ -1717,10 +1151,4 @@ public class BubbleController implements Bubbles, ConfigurationController.Config
}
}
}
-
- static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
- return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
- e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
- e.shouldSuppressPeek());
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index b4626f27d370..8cacc8f2ef01 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -34,7 +34,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController.DismissReason;
+import com.android.systemui.bubbles.Bubbles.DismissReason;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -137,8 +137,8 @@ public class BubbleData {
private Listener mListener;
@Nullable
- private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
- private BubbleController.PendingIntentCanceledListener mCancelledListener;
+ private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
+ private Bubbles.PendingIntentCanceledListener mCancelledListener;
/**
* We track groups with summaries that aren't visibly displayed but still kept around because
@@ -165,12 +165,12 @@ public class BubbleData {
}
public void setSuppressionChangedListener(
- BubbleController.NotificationSuppressionChangedListener listener) {
+ Bubbles.NotificationSuppressionChangedListener listener) {
mSuppressionListener = listener;
}
public void setPendingIntentCancelledListener(
- BubbleController.PendingIntentCanceledListener listener) {
+ Bubbles.PendingIntentCanceledListener listener) {
mCancelledListener = listener;
}
@@ -344,7 +344,8 @@ public class BubbleData {
/**
* Whether the summary for the provided group key is suppressed.
*/
- boolean isSummarySuppressed(String groupKey) {
+ @VisibleForTesting
+ public boolean isSummarySuppressed(String groupKey) {
return mSuppressedGroupKeys.containsKey(groupKey);
}
@@ -415,7 +416,7 @@ public class BubbleData {
// skip the selected bubble
.filter((b) -> !b.equals(mSelectedBubble))
.findFirst()
- .ifPresent((b) -> doRemove(b.getKey(), BubbleController.DISMISS_AGED));
+ .ifPresent((b) -> doRemove(b.getKey(), Bubbles.DISMISS_AGED));
}
}
@@ -459,12 +460,12 @@ public class BubbleData {
int indexToRemove = indexForKey(key);
if (indexToRemove == -1) {
if (hasOverflowBubbleWithKey(key)
- && (reason == BubbleController.DISMISS_NOTIF_CANCEL
- || reason == BubbleController.DISMISS_GROUP_CANCELLED
- || reason == BubbleController.DISMISS_NO_LONGER_BUBBLE
- || reason == BubbleController.DISMISS_BLOCKED
- || reason == BubbleController.DISMISS_SHORTCUT_REMOVED
- || reason == BubbleController.DISMISS_PACKAGE_REMOVED)) {
+ && (reason == Bubbles.DISMISS_NOTIF_CANCEL
+ || reason == Bubbles.DISMISS_GROUP_CANCELLED
+ || reason == Bubbles.DISMISS_NO_LONGER_BUBBLE
+ || reason == Bubbles.DISMISS_BLOCKED
+ || reason == Bubbles.DISMISS_SHORTCUT_REMOVED
+ || reason == Bubbles.DISMISS_PACKAGE_REMOVED)) {
Bubble b = getOverflowBubbleWithKey(key);
if (DEBUG_BUBBLE_DATA) {
@@ -512,8 +513,8 @@ public class BubbleData {
void overflowBubble(@DismissReason int reason, Bubble bubble) {
if (bubble.getPendingIntentCanceled()
- || !(reason == BubbleController.DISMISS_AGED
- || reason == BubbleController.DISMISS_USER_GESTURE)) {
+ || !(reason == Bubbles.DISMISS_AGED
+ || reason == Bubbles.DISMISS_USER_GESTURE)) {
return;
}
if (DEBUG_BUBBLE_DATA) {
@@ -529,7 +530,7 @@ public class BubbleData {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "Overflow full. Remove: " + oldest);
}
- mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED);
+ mStateChange.bubbleRemoved(oldest, Bubbles.DISMISS_OVERFLOW_MAX_REACHED);
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED);
mOverflowBubbles.remove(oldest);
mStateChange.removedOverflowBubble = oldest;
@@ -694,7 +695,7 @@ public class BubbleData {
}
private void maybeSendDeleteIntent(@DismissReason int reason, @NonNull final Bubble bubble) {
- if (reason != BubbleController.DISMISS_USER_GESTURE) return;
+ if (reason != Bubbles.DISMISS_USER_GESTURE) return;
PendingIntent deleteIntent = bubble.getDeleteIntent();
if (deleteIntent == null) return;
try {
@@ -726,7 +727,7 @@ public class BubbleData {
* The set of bubbles in overflow.
*/
@VisibleForTesting(visibility = PRIVATE)
- List<Bubble> getOverflowBubbles() {
+ public List<Bubble> getOverflowBubbles() {
return Collections.unmodifiableList(mOverflowBubbles);
}
@@ -742,7 +743,7 @@ public class BubbleData {
@VisibleForTesting(visibility = PRIVATE)
@Nullable
- Bubble getBubbleInStackWithKey(String key) {
+ public Bubble getBubbleInStackWithKey(String key) {
for (int i = 0; i < mBubbles.size(); i++) {
Bubble bubble = mBubbles.get(i);
if (bubble.getKey().equals(key)) {
@@ -764,7 +765,7 @@ public class BubbleData {
}
@VisibleForTesting(visibility = PRIVATE)
- Bubble getOverflowBubbleWithKey(String key) {
+ public Bubble getOverflowBubbleWithKey(String key) {
for (int i = 0; i < mOverflowBubbles.size(); i++) {
Bubble bubble = mOverflowBubbles.get(i);
if (bubble.getKey().equals(key)) {
@@ -788,7 +789,7 @@ public class BubbleData {
* This method should only be used in tests, not in production.
*/
@VisibleForTesting
- void setMaxOverflowBubbles(int maxOverflowBubbles) {
+ public void setMaxOverflowBubbles(int maxOverflowBubbles) {
mMaxOverflowBubbles = maxOverflowBubbles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
index d98fee399470..3937422750cc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
@@ -33,10 +33,10 @@ public class BubbleDebugConfig {
// to figure-out the origin of a log message while debugging the Bubbles a little painful. By
// setting this constant to true, log messages from the Bubbles package will be tagged with
// their class names instead fot the generic tag.
- static final boolean TAG_WITH_CLASS_NAME = false;
+ public static final boolean TAG_WITH_CLASS_NAME = false;
// Default log tag for the Bubbles package.
- static final String TAG_BUBBLES = "Bubbles";
+ public static final String TAG_BUBBLES = "Bubbles";
static final boolean DEBUG_BUBBLE_CONTROLLER = false;
static final boolean DEBUG_BUBBLE_DATA = false;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
index 6a1302518699..a0d3391f8347 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
@@ -16,6 +16,9 @@
package com.android.systemui.bubbles;
+import static android.app.Notification.FLAG_BUBBLE;
+
+import android.app.Notification;
import android.app.Notification.BubbleMetadata;
import android.app.NotificationManager.Policy;
import android.service.notification.NotificationListenerService.Ranking;
@@ -67,12 +70,45 @@ public class BubbleEntry {
return mSbn.getKey();
}
+ /** @return the group key in the {@link StatusBarNotification}. */
+ public String getGroupKey() {
+ return mSbn.getGroupKey();
+ }
+
/** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */
@Nullable
public BubbleMetadata getBubbleMetadata() {
return getStatusBarNotification().getNotification().getBubbleMetadata();
}
+ /**
+ * Updates the {@link Notification#FLAG_BUBBLE} flag on this notification to indicate
+ * whether it is a bubble or not. If this entry is set to not bubble, or does not have
+ * the required info to bubble, the flag cannot be set to true.
+ *
+ * @param shouldBubble whether this notification should be flagged as a bubble.
+ * @return true if the value changed.
+ */
+ public boolean setFlagBubble(boolean shouldBubble) {
+ boolean wasBubble = isBubble();
+ if (!shouldBubble) {
+ mSbn.getNotification().flags &= ~FLAG_BUBBLE;
+ } else if (getBubbleMetadata() != null && canBubble()) {
+ // wants to be bubble & can bubble, set flag
+ mSbn.getNotification().flags |= FLAG_BUBBLE;
+ }
+ return wasBubble != isBubble();
+ }
+
+ public boolean isBubble() {
+ return (mSbn.getNotification().flags & FLAG_BUBBLE) != 0;
+ }
+
+ /** @see Ranking#canBubble() */
+ public boolean canBubble() {
+ return mRanking.canBubble();
+ }
+
/** @return true if this notification is clearable. */
public boolean isClearable() {
return mIsClearable;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index cf2e13356cca..ae3c683cb165 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -54,7 +54,6 @@ import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
import com.android.systemui.statusbar.AlphaOptimizedButton;
@@ -99,7 +98,7 @@ public class BubbleExpandedView extends LinearLayout {
// TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead
private boolean mIsOverflow;
- private Bubbles mBubbles = Dependency.get(Bubbles.class);
+ private BubbleController mController;
private BubbleStackView mStackView;
private BubblePositioner mPositioner;
@@ -156,8 +155,7 @@ public class BubbleExpandedView extends LinearLayout {
// the bubble again so we'll just remove it.
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ ", " + e.getMessage() + "; removing bubble");
- mBubbles.removeBubble(getBubbleKey(),
- BubbleController.DISMISS_INVALID_INTENT);
+ mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
}
});
mInitialized = true;
@@ -195,15 +193,15 @@ public class BubbleExpandedView extends LinearLayout {
}
if (mBubble != null) {
// Must post because this is called from a binder thread.
- post(() -> mBubbles.removeBubble(mBubble.getKey(),
- BubbleController.DISMISS_TASK_FINISHED));
+ post(() -> mController.removeBubble(
+ mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED));
}
}
@Override
public void onBackPressedOnTaskRoot(int taskId) {
if (mTaskId == taskId && mStackView.isExpanded()) {
- mBubbles.collapseStack();
+ mController.collapseStack();
}
}
};
@@ -250,9 +248,6 @@ public class BubbleExpandedView extends LinearLayout {
R.dimen.bubble_manage_button_height);
mSettingsIcon = findViewById(R.id.settings_button);
- mPositioner = mBubbles.getPositioner();
-
- mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer());
// Set ActivityView's alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
@@ -263,7 +258,6 @@ public class BubbleExpandedView extends LinearLayout {
}
});
mExpandedViewContainer.setClipToOutline(true);
- mExpandedViewContainer.addView(mTaskView);
mExpandedViewContainer.setLayoutParams(
new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
addView(mExpandedViewContainer);
@@ -274,9 +268,7 @@ public class BubbleExpandedView extends LinearLayout {
// ==> expanded view
// ==> activity view
// ==> manage button
- bringChildToFront(mTaskView);
bringChildToFront(mSettingsIcon);
- mTaskView.setListener(mTaskViewListener);
applyThemeAttrs();
@@ -314,6 +306,20 @@ public class BubbleExpandedView extends LinearLayout {
super.onAttachedToWindow();
mTaskView.setExecutor(new HandlerExecutor(getHandler()));
}
+ /**
+ * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need
+ * to be called after view inflate.
+ */
+ void initialize(BubbleController controller, BubbleStackView stackView) {
+ mController = controller;
+ mStackView = stackView;
+
+ mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
+ mExpandedViewContainer.addView(mTaskView);
+ bringChildToFront(mTaskView);
+ mTaskView.setListener(mTaskViewListener);
+ mPositioner = mController.getPositioner();
+ }
void updateDimensions() {
Resources res = getResources();
@@ -464,16 +470,12 @@ public class BubbleExpandedView extends LinearLayout {
return mTaskId;
}
- void setStackView(BubbleStackView stackView) {
- mStackView = stackView;
- }
-
public void setOverflow(boolean overflow) {
mIsOverflow = overflow;
Intent target = new Intent(mContext, BubbleOverflowActivity.class);
Bundle extras = new Bundle();
- extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mBubbles));
+ extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mController));
target.putExtras(extras);
mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
target, PendingIntent.FLAG_UPDATE_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
index 48c809d1b0a7..a24f5c2b54c5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
@@ -91,14 +91,14 @@ public class BubbleLogger {
* @param b Bubble removed from overflow
* @param r Reason that bubble was removed
*/
- public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) {
- if (r == BubbleController.DISMISS_NOTIF_CANCEL) {
+ public void logOverflowRemove(Bubble b, @Bubbles.DismissReason int r) {
+ if (r == Bubbles.DISMISS_NOTIF_CANCEL) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
- } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) {
+ } else if (r == Bubbles.DISMISS_GROUP_CANCELLED) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL);
- } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) {
+ } else if (r == Bubbles.DISMISS_NO_LONGER_BUBBLE) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE);
- } else if (r == BubbleController.DISMISS_BLOCKED) {
+ } else if (r == Bubbles.DISMISS_BLOCKED) {
log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED);
}
}
@@ -107,10 +107,10 @@ public class BubbleLogger {
* @param b Bubble added to overflow
* @param r Reason that bubble was added to overflow
*/
- public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) {
- if (r == BubbleController.DISMISS_AGED) {
+ public void logOverflowAdd(Bubble b, @Bubbles.DismissReason int r) {
+ if (r == Bubbles.DISMISS_AGED) {
log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
- } else if (r == BubbleController.DISMISS_USER_GESTURE) {
+ } else if (r == Bubbles.DISMISS_USER_GESTURE) {
log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
index 102055de2bea..297144a86143 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -35,6 +35,7 @@ import com.android.systemui.R
class BubbleOverflow(
private val context: Context,
+ private val controller: BubbleController,
private val stack: BubbleStackView
) : BubbleViewProvider {
@@ -56,8 +57,8 @@ class BubbleOverflow(
init {
updateResources()
with(expandedView) {
+ initialize(controller, stack)
setOverflow(true)
- setStackView(stack)
applyThemeAttrs()
}
with(overflowBtn) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index fc3f5b6cbf5e..bc841730833c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -63,7 +63,7 @@ public class BubbleOverflowActivity extends Activity {
private TextView mEmptyStateTitle;
private TextView mEmptyStateSubtitle;
private ImageView mEmptyStateImage;
- private Bubbles mBubbles;
+ private BubbleController mController;
private BubbleOverflowAdapter mAdapter;
private RecyclerView mRecyclerView;
private List<Bubble> mOverflowBubbles = new ArrayList<>();
@@ -111,7 +111,7 @@ public class BubbleOverflowActivity extends Activity {
if (intent != null && intent.getExtras() != null) {
IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER);
if (binder instanceof ObjectWrapper) {
- mBubbles = ((ObjectWrapper<Bubbles>) binder).get();
+ mController = ((ObjectWrapper<BubbleController>) binder).get();
}
} else {
Log.w(TAG, "Bubble overflow activity created without bubble controller!");
@@ -139,15 +139,15 @@ public class BubbleOverflowActivity extends Activity {
final int viewHeight = recyclerViewHeight / rows;
mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
- mBubbles::promoteBubbleFromOverflow, viewWidth, viewHeight);
+ mController::promoteBubbleFromOverflow, viewWidth, viewHeight);
mRecyclerView.setAdapter(mAdapter);
mOverflowBubbles.clear();
- mOverflowBubbles.addAll(mBubbles.getOverflowBubbles());
+ mOverflowBubbles.addAll(mController.getOverflowBubbles());
mAdapter.notifyDataSetChanged();
updateEmptyStateVisibility();
- mBubbles.setOverflowListener(mDataListener);
+ mController.setOverflowListener(mDataListener);
updateTheme();
}
@@ -217,7 +217,7 @@ public class BubbleOverflowActivity extends Activity {
if (DEBUG_OVERFLOW) {
Log.d(TAG, BubbleDebugConfig.formatBubblesString(
- mBubbles.getOverflowBubbles(), null));
+ mController.getOverflowBubbles(), null));
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1201d42b1fc5..69ed5b72c1b2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -58,7 +58,6 @@ import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -155,7 +154,7 @@ public class BubbleStackView extends FrameLayout
*
* {@hide}
*/
- interface SurfaceSynchronizer {
+ public interface SurfaceSynchronizer {
/**
* Wait until requested change on a {@link View} is reflected on the screen.
*
@@ -186,7 +185,7 @@ public class BubbleStackView extends FrameLayout
});
}
};
-
+ private final BubbleController mBubbleController;
private final BubbleData mBubbleData;
private final ValueAnimator mDesaturateAndDarkenAnimator;
@@ -384,22 +383,6 @@ public class BubbleStackView extends FrameLayout
private final SurfaceSynchronizer mSurfaceSynchronizer;
/**
- * Callback to run when the IME visibility changes - BubbleController uses this to update the
- * Bubbles window focusability flags with the WindowManager.
- */
- public final Consumer<Boolean> mOnImeVisibilityChanged;
-
- /**
- * Callback to run when the bubble expand status changes.
- */
- private final Consumer<Boolean> mOnBubbleExpandChanged;
-
- /**
- * Callback to run to ask BubbleController to hide the current IME.
- */
- private final Runnable mHideCurrentInputMethodCallback;
-
- /**
* The currently magnetized object, which is being dragged and will be attracted to the magnetic
* dismiss target.
*
@@ -733,16 +716,12 @@ public class BubbleStackView extends FrameLayout
private BubblePositioner mPositioner;
@SuppressLint("ClickableViewAccessibility")
- public BubbleStackView(Context context, BubbleData data,
- @Nullable SurfaceSynchronizer synchronizer,
- FloatingContentCoordinator floatingContentCoordinator,
- Runnable allBubblesAnimatedOutAction,
- Consumer<Boolean> onImeVisibilityChanged,
- Runnable hideCurrentInputMethodCallback,
- Consumer<Boolean> onBubbleExpandChanged,
- BubblePositioner positioner) {
+ public BubbleStackView(Context context, BubbleController bubbleController,
+ BubbleData data, @Nullable SurfaceSynchronizer synchronizer,
+ FloatingContentCoordinator floatingContentCoordinator) {
super(context);
+ mBubbleController = bubbleController;
mBubbleData = data;
Resources res = getResources();
@@ -755,7 +734,7 @@ public class BubbleStackView extends FrameLayout
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
- mPositioner = positioner;
+ mPositioner = mBubbleController.getPositioner();
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
@@ -767,7 +746,7 @@ public class BubbleStackView extends FrameLayout
final Runnable onBubbleAnimatedOut = () -> {
if (getBubbleCount() == 0) {
- allBubblesAnimatedOutAction.run();
+ mBubbleController.onAllBubblesAnimatedOut();
}
};
@@ -839,7 +818,7 @@ public class BubbleStackView extends FrameLayout
setFocusable(true);
mBubbleContainer.bringToFront();
- mBubbleOverflow = new BubbleOverflow(getContext(), this);
+ mBubbleOverflow = new BubbleOverflow(getContext(), bubbleController, this);
mBubbleContainer.addView(mBubbleOverflow.getIconView(),
mBubbleContainer.getChildCount() /* index */,
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
@@ -850,12 +829,9 @@ public class BubbleStackView extends FrameLayout
showManageMenu(false);
});
- mOnImeVisibilityChanged = onImeVisibilityChanged;
- mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
- mOnBubbleExpandChanged = onBubbleExpandChanged;
-
setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
- onImeVisibilityChanged.accept(insets.getInsets(WindowInsets.Type.ime()).bottom > 0);
+ mBubbleController.onImeVisibilityChanged(
+ insets.getInsets(WindowInsets.Type.ime()).bottom > 0);
if (!mIsExpanded || mIsExpansionAnimating) {
return view.onApplyWindowInsets(insets);
}
@@ -1112,9 +1088,6 @@ public class BubbleStackView extends FrameLayout
}
mFlyout = new BubbleFlyoutView(getContext());
mFlyout.setVisibility(GONE);
- mFlyout.animate()
- .setDuration(FLYOUT_ALPHA_ANIMATION_DURATION)
- .setInterpolator(new AccelerateDecelerateInterpolator());
mFlyout.setOnClickListener(mFlyoutClickListener);
mFlyout.setOnTouchListener(mFlyoutTouchListener);
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
@@ -1299,7 +1272,7 @@ public class BubbleStackView extends FrameLayout
// R constants are not final so we cannot use switch-case here.
if (action == AccessibilityNodeInfo.ACTION_DISMISS) {
- mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION);
+ mBubbleData.dismissAll(Bubbles.DISMISS_ACCESSIBILITY_ACTION);
announceForAccessibility(
getResources().getString(R.string.accessibility_bubble_dismissed));
return true;
@@ -1402,8 +1375,9 @@ public class BubbleStackView extends FrameLayout
/**
* The {@link Bubble} that is expanded, null if one does not exist.
*/
+ @VisibleForTesting
@Nullable
- BubbleViewProvider getExpandedBubble() {
+ public BubbleViewProvider getExpandedBubble() {
return mExpandedBubble;
}
@@ -1617,7 +1591,7 @@ public class BubbleStackView extends FrameLayout
hideCurrentInputMethod();
- mOnBubbleExpandChanged.accept(shouldExpand);
+ mBubbleController.getSysuiProxy().onStackExpandChanged(shouldExpand);
if (mIsExpanded) {
animateCollapse();
@@ -1637,7 +1611,7 @@ public class BubbleStackView extends FrameLayout
* not.
*/
void hideCurrentInputMethod() {
- mHideCurrentInputMethodCallback.run();
+ mBubbleController.hideCurrentInputMethod();
}
private void beforeExpandedViewAnimation() {
@@ -2135,14 +2109,13 @@ public class BubbleStackView extends FrameLayout
final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject();
dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView));
} else {
- mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
}
}
private void dismissBubbleIfExists(@Nullable Bubble bubble) {
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
- mBubbleData.dismissBubbleWithKey(
- bubble.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(bubble.getKey(), Bubbles.DISMISS_USER_GESTURE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 010a29e3560a..a3e6a1ecc387 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -66,6 +66,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
private Bubble mBubble;
private WeakReference<Context> mContext;
+ private WeakReference<BubbleController> mController;
private WeakReference<BubbleStackView> mStackView;
private BubbleIconFactory mIconFactory;
private boolean mSkipInflation;
@@ -77,12 +78,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
*/
BubbleViewInfoTask(Bubble b,
Context context,
+ BubbleController controller,
BubbleStackView stackView,
BubbleIconFactory factory,
boolean skipInflation,
Callback c) {
mBubble = b;
mContext = new WeakReference<>(context);
+ mController = new WeakReference<>(controller);
mStackView = new WeakReference<>(stackView);
mIconFactory = factory;
mSkipInflation = skipInflation;
@@ -91,8 +94,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
@Override
protected BubbleViewInfo doInBackground(Void... voids) {
- return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble,
- mSkipInflation);
+ return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(),
+ mIconFactory, mBubble, mSkipInflation);
}
@Override
@@ -121,8 +124,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
Bubble.FlyoutMessage flyoutMessage;
@Nullable
- static BubbleViewInfo populate(Context c, BubbleStackView stackView,
- BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) {
+ static BubbleViewInfo populate(Context c, BubbleController controller,
+ BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b,
+ boolean skipInflation) {
BubbleViewInfo info = new BubbleViewInfo();
// View inflation: only should do this once per bubble
@@ -133,7 +137,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
info.expandedView = (BubbleExpandedView) inflater.inflate(
R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
- info.expandedView.setStackView(stackView);
+ info.expandedView.initialize(controller, stackView);
}
if (b.getShortcutInfo() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index 5cc24ce5a775..589017242311 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -26,7 +26,7 @@ import androidx.annotation.Nullable;
/**
* Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
*/
-interface BubbleViewProvider {
+public interface BubbleViewProvider {
@Nullable BubbleExpandedView getExpandedView();
void setContentVisibility(boolean visible);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
index 4882abc51ed6..415edb1b647c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
@@ -16,62 +16,93 @@
package com.android.systemui.bubbles;
-import android.annotation.NonNull;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
-import androidx.annotation.MainThread;
+import android.content.res.Configuration;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.util.ArraySet;
+import android.view.View;
-import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.wm.shell.ShellTaskOrganizer;
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
import java.util.List;
+import java.util.function.IntConsumer;
/**
* Interface to engage bubbles feature.
*/
public interface Bubbles {
+ @Retention(SOURCE)
+ @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
+ DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
+ DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
+ DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
+ DISMISS_NO_BUBBLE_UP})
+ @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
+ @interface DismissReason {}
+
+ int DISMISS_USER_GESTURE = 1;
+ int DISMISS_AGED = 2;
+ int DISMISS_TASK_FINISHED = 3;
+ int DISMISS_BLOCKED = 4;
+ int DISMISS_NOTIF_CANCEL = 5;
+ int DISMISS_ACCESSIBILITY_ACTION = 6;
+ int DISMISS_NO_LONGER_BUBBLE = 7;
+ int DISMISS_USER_CHANGED = 8;
+ int DISMISS_GROUP_CANCELLED = 9;
+ int DISMISS_INVALID_INTENT = 10;
+ int DISMISS_OVERFLOW_MAX_REACHED = 11;
+ int DISMISS_SHORTCUT_REMOVED = 12;
+ int DISMISS_PACKAGE_REMOVED = 13;
+ int DISMISS_NO_BUBBLE_UP = 14;
+
/**
* @return {@code true} if there is a bubble associated with the provided key and if its
* notification is hidden from the shade or there is a group summary associated with the
* provided key that is hidden from the shade because it has been dismissed but still has child
* bubbles active.
*/
- boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry);
+ boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey);
/**
* @return {@code true} if the current notification entry same as selected bubble
* notification entry and the stack is currently expanded.
*/
- boolean isBubbleExpanded(NotificationEntry entry);
+ boolean isBubbleExpanded(String key);
/** @return {@code true} if stack of bubbles is expanded or not. */
boolean isStackExpanded();
+ /** @return {@code true} if the summary for the provided group key is suppressed. */
+ boolean isSummarySuppressed(String groupKey);
+
/**
- * @return the {@link ScrimView} drawn behind the bubble stack. This is managed by
- * {@link ScrimController} since we want the scrim's appearance and behavior to be identical to
- * that of the notification shade scrim.
+ * Removes a group key indicating that summary for this group should no longer be suppressed.
*/
- ScrimView getScrimForBubble();
-
- /** @return Bubbles for updating overflow. */
- List<Bubble> getOverflowBubbles();
+ void removeSuppressedSummary(String groupKey);
/** Tell the stack of bubbles to collapse. */
void collapseStack();
+ /** Tell the controller need update its UI to fit theme. */
+ void updateForThemeChanges();
+
/**
* Request the stack expand if needed, then select the specified Bubble as current.
* If no bubble exists for this entry, one is created.
*
* @param entry the notification for the bubble to be selected
*/
- void expandStackAndSelectBubble(NotificationEntry entry);
-
- /** Promote the provided bubbles when overflow view. */
- void promoteBubbleFromOverflow(Bubble bubble);
+ void expandStackAndSelectBubble(BubbleEntry entry);
/**
* We intercept notification entries (including group summaries) dismissed by the user when
@@ -81,26 +112,65 @@ public interface Bubbles {
* {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add
* {@link BubbleData#addSummaryToSuppress}.
*
+ * @param entry the notification of the BubbleEntry should be removed.
+ * @param children the list of child notification of the BubbleEntry from 1st param entry,
+ * this will be null if entry does have no children.
+ * @param removeCallback the remove callback for SystemUI side to remove notification, the int
+ * number should be list position of children list and use -1 for
+ * removing the parent notification.
+ *
* @return true if we want to intercept the dismissal of the entry, else false.
*/
- boolean handleDismissalInterception(NotificationEntry entry);
+ boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children,
+ IntConsumer removeCallback);
/**
- * Removes the bubble with the given key.
- * <p>
- * Must be called from the main thread.
+ * Retrieves the notif entry key of the summary associated with the provided group key.
+ *
+ * @param groupKey the group to look up
+ * @return the key for the notification that is the summary of this group.
*/
- @MainThread
- void removeBubble(String key, int reason);
+ String getSummaryKey(String groupKey);
+
+ /** Set the proxy to commnuicate with SysUi side components. */
+ void setSysuiProxy(SysuiProxy proxy);
+
+ /** Set the scrim view for bubbles. */
+ void setBubbleScrim(View view);
+
+ /** Set a listener to be notified of bubble expand events. */
+ void setExpandListener(BubbleExpandListener listener);
+ /**
+ * Called when new notification entry added.
+ *
+ * @param entry the {@link BubbleEntry} by the notification.
+ */
+ void onEntryAdded(BubbleEntry entry);
+
+ /**
+ * Called when new notification entry updated.
+ *
+ * @param entry the {@link BubbleEntry} by the notification.
+ * @param shouldBubbleUp {@code true} if this notification should bubble up.
+ */
+ void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp);
+
+ /**
+ * Called when new notification entry removed.
+ *
+ * @param entry the {@link BubbleEntry} by the notification.
+ */
+ void onEntryRemoved(BubbleEntry entry);
/**
- * When a notification is marked Priority, expand the stack if needed,
- * then (maybe create and) select the given bubble.
+ * Called when NotificationListener has received adjusted notification rank and reapplied
+ * filtering and sorting. This is used to dismiss or create bubbles based on changes in
+ * permissions on the notification channel or the global setting.
*
- * @param entry the notification for the bubble to show
+ * @param rankingMap the updated ranking map from NotificationListenerService
*/
- void onUserChangedImportance(NotificationEntry entry);
+ void onRankingUpdated(RankingMap rankingMap);
/**
* Called when the status bar has become visible or invisible (either permanently or
@@ -108,30 +178,85 @@ public interface Bubbles {
*/
void onStatusBarVisibilityChanged(boolean visible);
+ /** Called when system zen mode state changed. */
+ void onZenStateChanged();
+
/**
- * Called when a user has indicated that an active notification should be shown as a bubble.
- * <p>
- * This method will collapse the shade, create the bubble without a flyout or dot, and suppress
- * the notification from appearing in the shade.
+ * Called when statusBar state changed.
*
- * @param entry the notification to change bubble state for.
- * @param shouldBubble whether the notification should show as a bubble or not.
+ * @param isShade {@code true} is state is SHADE.
*/
- void onUserChangedBubble(@NonNull NotificationEntry entry, boolean shouldBubble);
+ void onStatusBarStateChanged(boolean isShade);
+ /**
+ * Called when the current user changed.
+ *
+ * @param newUserId the new user's id.
+ */
+ void onUserChanged(int newUserId);
- /** See {@link BubbleController.NotifCallback}. */
- void addNotifCallback(BubbleController.NotifCallback callback);
+ /**
+ * Called when config changed.
+ *
+ * @param newConfig the new config.
+ */
+ void onConfigChanged(Configuration newConfig);
- /** Set a listener to be notified of bubble expand events. */
- void setExpandListener(BubbleController.BubbleExpandListener listener);
+ /** Description of current bubble state. */
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ /** Listener to find out about stack expansion / collapse events. */
+ interface BubbleExpandListener {
+ /**
+ * Called when the expansion state of the bubble stack changes.
+ *
+ * @param isExpanding whether it's expanding or collapsing
+ * @param key the notification key associated with bubble being expanded
+ */
+ void onBubbleExpandChanged(boolean isExpanding, String key);
+ }
+
+ /** Listener to be notified when a bubbles' notification suppression state changes.*/
+ interface NotificationSuppressionChangedListener {
+ /** Called when the notification suppression state of a bubble changes. */
+ void onBubbleNotificationSuppressionChange(Bubble bubble);
+ }
+
+ /** Listener to be notified when a pending intent has been canceled for a bubble. */
+ interface PendingIntentCanceledListener {
+ /** Called when the pending intent for a bubble has been canceled. */
+ void onPendingIntentCanceled(Bubble bubble);
+ }
+
+ /** Callback to tell SysUi components execute some methods. */
+ interface SysuiProxy {
+ @Nullable
+ BubbleEntry getPendingOrActiveEntry(String key);
+
+ List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys);
+
+ boolean isNotificationShadeExpand();
+
+ boolean shouldBubbleUp(String key);
+
+ void setNotificationInterruption(String key);
+
+ void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag);
+
+ void notifyRemoveNotification(String key, int reason);
+
+ void notifyInvalidateNotifications(String reason);
+
+ void notifyMaybeCancelSummary(String key);
+
+ void removeNotificationEntry(String key);
+
+ void updateNotificationBubbleButton(String key);
- /** Set a listener to be notified of when overflow view update. */
- void setOverflowListener(BubbleData.Listener listener);
+ void updateNotificationSuppression(String key);
- /** The task listener for events in bubble tasks. **/
- ShellTaskOrganizer getTaskOrganizer();
+ void onStackExpandChanged(boolean shouldExpand);
- /** Contains information to help position things on the screen. */
- BubblePositioner getPositioner();
+ void onUnbubbleConversation(String key);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index 6e6f82b714ff..0a596d5c69b6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -27,6 +27,7 @@ import android.util.FloatProperty;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
@@ -385,7 +386,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
View view, DynamicAnimation.ViewProperty... properties) {
final ObjectAnimator targetAnimator = getTargetAnimatorFromView(view);
for (DynamicAnimation.ViewProperty property : properties) {
- final SpringAnimation animation = getAnimationFromView(property, view);
+ final SpringAnimation animation = getSpringAnimationFromView(property, view);
if (animation != null && animation.isRunning()) {
return true;
}
@@ -422,11 +423,15 @@ public class PhysicsAnimationLayout extends FrameLayout {
for (int i = 0; i < getChildCount(); i++) {
for (DynamicAnimation.ViewProperty property : properties) {
- final DynamicAnimation anim = getAnimationAtIndex(property, i);
+ final DynamicAnimation anim = getSpringAnimationAtIndex(property, i);
if (anim != null) {
anim.cancel();
}
}
+ final ViewPropertyAnimator anim = getViewPropertyAnimatorFromView(getChildAt(i));
+ if (anim != null) {
+ anim.cancel();
+ }
}
}
@@ -441,7 +446,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
// Cancel physics animations on the view.
for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) {
- final DynamicAnimation animationFromView = getAnimationFromView(property, view);
+ final DynamicAnimation animationFromView = getSpringAnimationFromView(property, view);
if (animationFromView != null) {
animationFromView.cancel();
}
@@ -502,17 +507,27 @@ public class PhysicsAnimationLayout extends FrameLayout {
* Retrieves the animation of the given property from the view at the given index via the view
* tag system.
*/
- @Nullable private SpringAnimation getAnimationAtIndex(
+ @Nullable private SpringAnimation getSpringAnimationAtIndex(
DynamicAnimation.ViewProperty property, int index) {
- return getAnimationFromView(property, getChildAt(index));
+ return getSpringAnimationFromView(property, getChildAt(index));
}
- /** Retrieves the animation of the given property from the view via the view tag system. */
- @Nullable private SpringAnimation getAnimationFromView(
+ /**
+ * Retrieves the spring animation of the given property from the view via the view tag system.
+ */
+ @Nullable private SpringAnimation getSpringAnimationFromView(
DynamicAnimation.ViewProperty property, View view) {
return (SpringAnimation) view.getTag(getTagIdForProperty(property));
}
+ /**
+ * Retrieves the view property animation of the given property from the view via the view tag
+ * system.
+ */
+ @Nullable private ViewPropertyAnimator getViewPropertyAnimatorFromView(View view) {
+ return (ViewPropertyAnimator) view.getTag(R.id.reorder_animator_tag);
+ }
+
/** Retrieves the target animator from the view via the view tag system. */
@Nullable private ObjectAnimator getTargetAnimatorFromView(View view) {
return (ObjectAnimator) view.getTag(R.id.target_animator_tag);
@@ -539,7 +554,8 @@ public class PhysicsAnimationLayout extends FrameLayout {
final float offset = mController.getOffsetForChainedPropertyAnimation(property);
if (nextAnimInChain < getChildCount()) {
- final SpringAnimation nextAnim = getAnimationAtIndex(property, nextAnimInChain);
+ final SpringAnimation nextAnim = getSpringAnimationAtIndex(
+ property, nextAnimInChain);
if (nextAnim != null) {
nextAnim.animateToFinalPosition(value + offset);
}
@@ -902,9 +918,9 @@ public class PhysicsAnimationLayout extends FrameLayout {
// and TRANSLATION_Y animations ending, and call them once both have finished.
if (mPositionEndActions != null) {
final SpringAnimation translationXAnim =
- getAnimationFromView(DynamicAnimation.TRANSLATION_X, mView);
+ getSpringAnimationFromView(DynamicAnimation.TRANSLATION_X, mView);
final SpringAnimation translationYAnim =
- getAnimationFromView(DynamicAnimation.TRANSLATION_Y, mView);
+ getSpringAnimationFromView(DynamicAnimation.TRANSLATION_Y, mView);
final Runnable waitForBothXAndY = () -> {
if (!translationXAnim.isRunning() && !translationYAnim.isRunning()) {
if (mPositionEndActions != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index c410b8267dd4..43893f215d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -24,6 +24,7 @@ import android.graphics.RectF;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
+import android.view.ViewPropertyAnimator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -782,11 +783,11 @@ public class StackAnimationController extends
}
}
- public void animateReorder(List<View> bubbleViews, Runnable after) {
+ public void animateReorder(List<View> bubbleViews, Runnable after) {
for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) {
View view = bubbleViews.get(newIndex);
- final int oldIndex= mLayout.indexOfChild(view);
- animateSwap(view, oldIndex, newIndex, after);
+ final int oldIndex = mLayout.indexOfChild(view);
+ animateSwap(view, oldIndex, newIndex, after);
}
}
@@ -795,12 +796,15 @@ public class StackAnimationController extends
final float swapY = newIndex == 0
? newY - mSwapAnimationOffset // Above top of stack
: newY + mSwapAnimationOffset; // Below where bubble will be
- view.animate()
+ final ViewPropertyAnimator animator = view.animate()
.scaleX(BUBBLE_SWAP_SCALE)
.scaleY(BUBBLE_SWAP_SCALE)
.translationY(swapY)
.setDuration(BUBBLE_SWAP_DURATION)
- .withEndAction(() -> finishSwapAnimation(view, oldIndex, newIndex, finishReorder));
+ .withEndAction(() -> {
+ finishSwapAnimation(view, oldIndex, newIndex, finishReorder);
+ });
+ view.setTag(R.id.reorder_animator_tag, animator);
}
private void finishSwapAnimation(View view, int oldIndex, int newIndex,
@@ -818,12 +822,16 @@ public class StackAnimationController extends
// Animate bubble back into stack, at new index and original size.
final float newY = getStackPosition().y + newIndex * mStackOffset;
- view.animate()
+ final ViewPropertyAnimator animator = view.animate()
.scaleX(1f)
.scaleY(1f)
.translationY(newY)
.setDuration(BUBBLE_SWAP_DURATION)
- .withEndAction(() -> finishReorder.run());
+ .withEndAction(() -> {
+ view.setTag(R.id.reorder_animator_tag, null);
+ finishReorder.run();
+ });
+ view.setTag(R.id.reorder_animator_tag, animator);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 6b5f237ac76f..5a7e033607f8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -22,6 +22,8 @@ import android.content.pm.LauncherApps;
import android.os.Handler;
import android.view.WindowManager;
+import androidx.annotation.Nullable;
+
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.bubbles.BubbleController;
@@ -41,10 +43,13 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import java.util.Optional;
+
import dagger.Module;
import dagger.Provides;
@@ -56,23 +61,8 @@ public interface BubbleModule {
*/
@SysUISingleton
@Provides
- static Bubbles newBubbleController(
- Context context,
- NotificationShadeWindowController notificationShadeWindowController,
- StatusBarStateController statusBarStateController,
- ShadeController shadeController,
- ConfigurationController configurationController,
- NotificationInterruptStateProvider interruptionStateProvider,
- ZenModeController zenModeController,
- NotificationLockscreenUserManager notifUserManager,
- NotificationGroupManagerLegacy groupManager,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- FeatureFlags featureFlags,
- DumpManager dumpManager,
+ static Bubbles newBubbleController(Context context,
FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState,
- INotificationManager notifManager,
IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -80,30 +70,29 @@ public interface BubbleModule {
UiEventLogger uiEventLogger,
@Main Handler mainHandler,
ShellTaskOrganizer organizer) {
- return BubbleController.create(
- context,
- notificationShadeWindowController,
- statusBarStateController,
- shadeController,
- null /* synchronizer */,
- configurationController,
- interruptionStateProvider,
- zenModeController,
- notifUserManager,
- groupManager,
- entryManager,
- notifPipeline,
- featureFlags,
- dumpManager,
- floatingContentCoordinator,
- sysUiState,
- notifManager,
- statusBarService,
- windowManager,
- windowManagerShellWrapper,
- launcherApps,
- uiEventLogger,
- mainHandler,
- organizer);
+ return BubbleController.create(context, null /* synchronizer */, floatingContentCoordinator,
+ statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
+ uiEventLogger, mainHandler, organizer);
+ }
+
+ /** Provides Optional of BubbleManager */
+ @SysUISingleton
+ @Provides
+ static Optional<BubblesManager> provideBubblesManager(Context context,
+ Optional<Bubbles> bubblesOptional,
+ NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarStateController statusBarStateController, ShadeController shadeController,
+ ConfigurationController configurationController,
+ @Nullable IStatusBarService statusBarService, INotificationManager notificationManager,
+ NotificationInterruptStateProvider interruptionStateProvider,
+ ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager,
+ NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline, SysUiState sysUiState, FeatureFlags featureFlags,
+ DumpManager dumpManager) {
+ return Optional.ofNullable(BubblesManager.create(context, bubblesOptional,
+ notificationShadeWindowController, statusBarStateController, shadeController,
+ configurationController, statusBarService, notificationManager,
+ interruptionStateProvider, zenModeController, notifUserManager,
+ groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 80928d6da978..451bd42bd053 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -16,10 +16,12 @@
package com.android.systemui.media.dialog;
+import android.app.Notification;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.MediaRoute2Info;
import android.media.RoutingSessionInfo;
@@ -221,9 +223,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
}
for (NotificationEntry entry
: mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
- if (entry.getSbn().getNotification().hasMediaSession()
+ final Notification notification = entry.getSbn().getNotification();
+ if (notification.hasMediaSession()
&& TextUtils.equals(entry.getSbn().getPackageName(), mPackageName)) {
- return IconCompat.createFromIcon(entry.getSbn().getNotification().getLargeIcon());
+ final Icon icon = notification.getLargeIcon();
+ if (icon == null) {
+ break;
+ }
+ return IconCompat.createFromIcon(icon);
}
}
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index 81076475c5ce..6ac1e7079531 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -99,7 +99,7 @@ class DoubleLineTileLayout(
}
}
- override fun getNumVisibleTiles() = tilesToShow
+ override fun getNumVisibleTiles() = Math.min(mRecords.size, tilesToShow)
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 1b17a2a277f2..76f244652cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -480,7 +480,6 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi
}
}
mTileLayout = newLayout;
- newLayout.setListening(mListening);
if (needsDynamicRowsAndColumns()) {
newLayout.setMinRows(horizontal ? 2 : 1);
// Let's use 3 columns to match the current layout
@@ -498,6 +497,14 @@ public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorLi
return false;
}
+ /**
+ * Sets the listening state of the current layout to the state of the view. Used after
+ * switching layouts.
+ */
+ public void reSetLayoutListening() {
+ mTileLayout.setListening(mListening);
+ }
+
private void updateHorizontalLinearLayoutMargins() {
if (mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) {
LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index fe92827806c6..68a6cdcbd289 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -214,6 +214,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
boolean switchTileLayout(boolean force) {
if (mView.switchTileLayout(force, mRecords)) {
setTiles();
+ mView.reSetLayoutListening();
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 84a5b6f0538d..ed0900d07b56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -336,7 +336,7 @@ public class QuickQSPanel extends QSPanel {
@Override
public int getNumVisibleTiles() {
- return mColumns;
+ return Math.min(mRecords.size(), mColumns);
}
@Override
@@ -353,6 +353,7 @@ public class QuickQSPanel extends QSPanel {
boolean startedListening = !mListening && listening;
super.setListening(listening);
if (startedListening) {
+ // getNumVisibleTiles() <= mRecords.size()
for (int i = 0; i < getNumVisibleTiles(); i++) {
QSTile tile = mRecords.get(i).tile;
mUiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index f063fbe7a384..260f55799e0b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -38,6 +38,7 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.media.MediaActionSound;
import android.net.Uri;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -143,6 +144,7 @@ public class ScreenshotController {
private final AccessibilityManager mAccessibilityManager;
private final MediaActionSound mCameraSound;
+ private final Binder mWindowToken;
private ScreenshotView mScreenshotView;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
@@ -178,13 +180,9 @@ public class ScreenshotController {
mNotificationsController = screenshotNotificationsController;
mUiEventLogger = uiEventLogger;
- // Create a visual (Window) context
- // After this, our windowToken is available from mContext.getActivityToken()
final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class));
mDisplay = dm.getDisplay(DEFAULT_DISPLAY);
- Context displayContext = context.createDisplayContext(mDisplay);
- mContext = new WindowContext(displayContext, WindowManager.LayoutParams.TYPE_SCREENSHOT,
- null);
+ mContext = context.createDisplayContext(mDisplay);
mWindowManager = mContext.getSystemService(WindowManager.class);
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
@@ -194,6 +192,7 @@ public class ScreenshotController {
mInDarkMode = config.isNightModeActive();
mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT;
+ mWindowToken = new Binder("ScreenshotController");
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
@@ -210,6 +209,7 @@ public class ScreenshotController {
mWindowLayoutParams.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
+ mWindowLayoutParams.token = mWindowToken;
mDisplayMetrics = new DisplayMetrics();
mDisplay.getRealMetrics(mDisplayMetrics);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 53179ba4be90..a92b9e4818b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -159,7 +159,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
for (int i = 0; i < N; i++) {
NotificationEntry ent = activeNotifications.get(i);
final boolean isBubbleNotificationSuppressedFromShade = mBubblesOptional.isPresent()
- && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(ent);
+ && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
+ ent.getKey(), ent.getSbn().getGroupKey());
if (ent.isRowDismissed() || ent.isRowRemoved()
|| isBubbleNotificationSuppressedFromShade
|| mFgsSectionController.hasEntry(ent)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index ddfa18e65ee0..44b9bd26aa38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -46,7 +46,7 @@ class ConversationNotificationProcessor @Inject constructor(
Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT
else
Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL
- entry.ranking.shortcutInfo?.let { shortcutInfo ->
+ entry.ranking.conversationShortcutInfo?.let { shortcutInfo ->
messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo)
shortcutInfo.label?.let { label ->
messagingStyle.conversationTitle = label
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
index c301002ef8d0..65e333f14df6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
@@ -47,7 +47,7 @@ public class NotificationChannelHelper {
final String pkg = entry.getSbn().getPackageName();
final int appUid = entry.getSbn().getUid();
if (TextUtils.isEmpty(conversationId) || TextUtils.isEmpty(pkg)
- || entry.getRanking().getShortcutInfo() == null) {
+ || entry.getRanking().getConversationShortcutInfo() == null) {
return channel;
}
@@ -68,8 +68,8 @@ public class NotificationChannelHelper {
}
private static CharSequence getName(NotificationEntry entry) {
- if (entry.getRanking().getShortcutInfo().getLabel() != null) {
- return entry.getRanking().getShortcutInfo().getLabel().toString();
+ if (entry.getRanking().getConversationShortcutInfo().getLabel() != null) {
+ return entry.getRanking().getConversationShortcutInfo().getLabel().toString();
}
Bundle extras = entry.getSbn().getNotification().extras;
CharSequence nameString = extras.getCharSequence(Notification.EXTRA_CONVERSATION_TITLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 0455b0f18afc..83a569bd6573 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -25,6 +25,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
+import com.android.systemui.wmshell.BubblesManager;
import java.util.HashSet;
import java.util.Optional;
@@ -56,6 +57,7 @@ import javax.inject.Inject;
public class BubbleCoordinator implements Coordinator {
private static final String TAG = "BubbleCoordinator";
+ private final Optional<BubblesManager> mBubblesManagerOptional;
private final Optional<Bubbles> mBubblesOptional;
private final NotifCollection mNotifCollection;
private final Set<String> mInterceptedDismissalEntries = new HashSet<>();
@@ -64,8 +66,10 @@ public class BubbleCoordinator implements Coordinator {
@Inject
public BubbleCoordinator(
+ Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
NotifCollection notifCollection) {
+ mBubblesManagerOptional = bubblesManagerOptional;
mBubblesOptional = bubblesOptional;
mNotifCollection = notifCollection;
}
@@ -75,8 +79,8 @@ public class BubbleCoordinator implements Coordinator {
mNotifPipeline = pipeline;
mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor);
mNotifPipeline.addFinalizeFilter(mNotifFilter);
- if (mBubblesOptional.isPresent()) {
- mBubblesOptional.get().addNotifCallback(mNotifCallback);
+ if (mBubblesManagerOptional.isPresent()) {
+ mBubblesManagerOptional.get().addNotifCallback(mNotifCallback);
}
}
@@ -85,7 +89,8 @@ public class BubbleCoordinator implements Coordinator {
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
return mBubblesOptional.isPresent()
- && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(entry);
+ && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getSbn().getGroupKey());
}
};
@@ -102,9 +107,8 @@ public class BubbleCoordinator implements Coordinator {
@Override
public boolean shouldInterceptDismissal(NotificationEntry entry) {
- // for experimental bubbles
- if (mBubblesOptional.isPresent()
- && mBubblesOptional.get().handleDismissalInterception(entry)) {
+ if (mBubblesManagerOptional.isPresent()
+ && mBubblesManagerOptional.get().handleDismissalInterception(entry)) {
mInterceptedDismissalEntries.add(entry.getKey());
return true;
} else {
@@ -119,8 +123,7 @@ public class BubbleCoordinator implements Coordinator {
}
};
- private final BubbleController.NotifCallback mNotifCallback =
- new BubbleController.NotifCallback() {
+ private final BubblesManager.NotifCallback mNotifCallback = new BubblesManager.NotifCallback() {
@Override
public void removeNotification(
NotificationEntry entry,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index 490989dbb39e..36adfac21bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -64,7 +64,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
new ArraySet<>();
private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
- private final Optional<Lazy<Bubbles>> mBubblesOptional;
+ private final Optional<Bubbles> mBubblesOptional;
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
@@ -74,7 +74,7 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
public NotificationGroupManagerLegacy(
StatusBarStateController statusBarStateController,
Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier,
- Optional<Lazy<Bubbles>> bubblesOptional) {
+ Optional<Bubbles> bubblesOptional) {
statusBarStateController.addCallback(this);
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
mBubblesOptional = bubblesOptional;
@@ -242,8 +242,9 @@ public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener,
int childCount = 0;
boolean hasBubbles = false;
for (NotificationEntry entry : group.children.values()) {
- if (mBubblesOptional.isPresent() && !mBubblesOptional.get().get()
- .isBubbleNotificationSuppressedFromShade(entry)) {
+ if (mBubblesOptional.isPresent() && !mBubblesOptional.get()
+ .isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getSbn().getGroupKey())) {
childCount++;
} else {
hasBubbles = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 4fff99b482d8..ff55cd60ab3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -26,7 +26,6 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -73,6 +72,7 @@ import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogC
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -133,7 +133,7 @@ public interface NotificationsModule {
UserContextProvider contextTracker,
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
AssistantFeedbackController assistantFeedbackController,
- Optional<Bubbles> bubblesOptional,
+ Optional<BubblesManager> bubblesManagerOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback) {
return new NotificationGutsManager(
@@ -150,7 +150,7 @@ public interface NotificationsModule {
contextTracker,
builderProvider,
assistantFeedbackController,
- bubblesOptional,
+ bubblesManagerOptional,
uiEventLogger,
onUserInteractionCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 13f7a53f5e54..ba45f9a687ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -255,7 +255,7 @@ class IconManager @Inject constructor(
private fun createPeopleAvatar(entry: NotificationEntry): Icon? {
var ic: Icon? = null
- val shortcut = entry.ranking.shortcutInfo
+ val shortcut = entry.ranking.conversationShortcutInfo
if (shortcut != null) {
ic = launcherApps.getShortcutIcon(shortcut)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index 99b2fcc9d610..cd9ba4e690e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -220,7 +220,7 @@ class PeopleHubDataSourceImpl @Inject constructor(
}
val clickRunnable = Runnable { notificationListener.unsnoozeNotification(key) }
val extras = sbn.notification.extras
- val name = ranking.shortcutInfo?.label
+ val name = ranking.conversationShortcutInfo?.label
?: extras.getCharSequence(Notification.EXTRA_CONVERSATION_TITLE)
?: extras.getCharSequence(Notification.EXTRA_TITLE)
?: return null
@@ -238,9 +238,9 @@ class PeopleHubDataSourceImpl @Inject constructor(
iconFactory: ConversationIconFactory,
sbn: StatusBarNotification
): Drawable? =
- shortcutInfo?.let { shortcutInfo ->
+ conversationShortcutInfo?.let { conversationShortcutInfo ->
iconFactory.getConversationDrawable(
- shortcutInfo,
+ conversationShortcutInfo,
sbn.packageName,
sbn.uid,
channel.isImportantConversation
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 0d92616767f3..691f1f452da8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -103,7 +103,7 @@ class PeopleNotificationIdentifierImpl @Inject constructor(
private val Ranking.personTypeInfo
get() = when {
!isConversation -> TYPE_NON_PERSON
- shortcutInfo == null -> TYPE_PERSON
+ conversationShortcutInfo == null -> TYPE_PERSON
channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON
else -> TYPE_FULL_PERSON
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5ee66fabe55a..280c525f41cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -72,7 +72,6 @@ import com.android.internal.widget.CachingIconView;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -102,12 +101,14 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
+import com.android.systemui.wmshell.BubblesManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -147,6 +148,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private LayoutListener mLayoutListener;
private RowContentBindStage mRowContentBindStage;
private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+ private Optional<BubblesManager> mBubblesManagerOptional;
private int mIconTransformContentShift;
private int mMaxHeadsUpHeightBeforeN;
private int mMaxHeadsUpHeightBeforeP;
@@ -1078,13 +1080,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
/** The click listener for the bubble button. */
public View.OnClickListener getBubbleClickListener() {
- return new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Dependency.get(Bubbles.class)
- .onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */);
- mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */);
+ return v -> {
+ if (mBubblesManagerOptional.isPresent()) {
+ mBubblesManagerOptional.get()
+ .onUserChangedBubble(mEntry, !mEntry.isBubble() /* createBubble */);
}
+ mHeadsUpManager.removeNotification(mEntry.getKey(), true /* releaseImmediately */);
};
}
@@ -1553,7 +1554,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
FalsingManager falsingManager,
StatusBarStateController statusBarStateController,
PeopleNotificationIdentifier peopleNotificationIdentifier,
- OnUserInteractionCallback onUserInteractionCallback) {
+ OnUserInteractionCallback onUserInteractionCallback,
+ Optional<BubblesManager> bubblesManagerOptional) {
mEntry = entry;
mAppName = appName;
if (mMenuRow == null) {
@@ -1581,6 +1583,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier);
}
mOnUserInteractionCallback = onUserInteractionCallback;
+ mBubblesManagerOptional = bubblesManagerOptional;
cacheIsSystemNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index c995e324ecfe..05b1dba36a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -43,8 +43,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.wmshell.BubblesManager;
import java.util.List;
+import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
@@ -79,6 +81,7 @@ public class ExpandableNotificationRowController implements NodeController {
private final FalsingManager mFalsingManager;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+ private final Optional<BubblesManager> mBubblesManagerOptional;
@Inject
public ExpandableNotificationRowController(
@@ -102,7 +105,8 @@ public class ExpandableNotificationRowController implements NodeController {
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
OnUserInteractionCallback onUserInteractionCallback,
FalsingManager falsingManager,
- PeopleNotificationIdentifier peopleNotificationIdentifier) {
+ PeopleNotificationIdentifier peopleNotificationIdentifier,
+ Optional<BubblesManager> bubblesManagerOptional) {
mView = view;
mListContainer = listContainer;
mActivatableNotificationViewController = activatableNotificationViewController;
@@ -125,6 +129,7 @@ public class ExpandableNotificationRowController implements NodeController {
mAllowLongPress = allowLongPress;
mFalsingManager = falsingManager;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
+ mBubblesManagerOptional = bubblesManagerOptional;
}
/**
@@ -148,8 +153,8 @@ public class ExpandableNotificationRowController implements NodeController {
mFalsingManager,
mStatusBarStateController,
mPeopleNotificationIdentifier,
- mOnUserInteractionCallback
-
+ mOnUserInteractionCallback,
+ mBubblesManagerOptional
);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 07a4a188bc48..e43130f1698b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -67,12 +67,12 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.wmshell.BubblesManager;
import java.lang.annotation.Retention;
import java.util.Optional;
@@ -94,7 +94,7 @@ public class NotificationConversationInfo extends LinearLayout implements
private OnUserInteractionCallback mOnUserInteractionCallback;
private Handler mMainHandler;
private Handler mBgHandler;
- private Optional<Bubbles> mBubblesOptional;
+ private Optional<BubblesManager> mBubblesManagerOptional;
private String mPackageName;
private String mAppName;
private int mAppUid;
@@ -223,7 +223,7 @@ public class NotificationConversationInfo extends LinearLayout implements
@Main Handler mainHandler,
@Background Handler bgHandler,
OnConversationSettingsClickListener onConversationSettingsClickListener,
- Optional<Bubbles> bubblesOptional) {
+ Optional<BubblesManager> bubblesManagerOptional) {
mSelectedAction = -1;
mINotificationManager = iNotificationManager;
mOnUserInteractionCallback = onUserInteractionCallback;
@@ -242,12 +242,12 @@ public class NotificationConversationInfo extends LinearLayout implements
mIconFactory = conversationIconFactory;
mUserContext = userContext;
mBubbleMetadata = bubbleMetadata;
- mBubblesOptional = bubblesOptional;
+ mBubblesManagerOptional = bubblesManagerOptional;
mBuilderProvider = builderProvider;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
mShortcutManager = shortcutManager;
- mShortcutInfo = entry.getRanking().getShortcutInfo();
+ mShortcutInfo = entry.getRanking().getConversationShortcutInfo();
if (mShortcutInfo == null) {
throw new IllegalArgumentException("Does not have required information");
}
@@ -641,9 +641,9 @@ public class NotificationConversationInfo extends LinearLayout implements
mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
BUBBLE_PREFERENCE_SELECTED);
}
- if (mBubblesOptional.isPresent()) {
+ if (mBubblesManagerOptional.isPresent()) {
post(() -> {
- mBubblesOptional.get().onUserChangedImportance(mEntry);
+ mBubblesManagerOptional.get().onUserChangedImportance(mEntry);
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 373f20e6ba96..d2cfb2908e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -47,7 +47,6 @@ import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -67,6 +66,7 @@ import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSav
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.wmshell.BubblesManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -117,7 +117,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
private final Lazy<StatusBar> mStatusBarLazy;
private final Handler mMainHandler;
private final Handler mBgHandler;
- private final Optional<Bubbles> mBubblesOptional;
+ private final Optional<BubblesManager> mBubblesManagerOptional;
private Runnable mOpenRunnable;
private final INotificationManager mNotificationManager;
private final LauncherApps mLauncherApps;
@@ -142,7 +142,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
UserContextProvider contextTracker,
Provider<PriorityOnboardingDialogController.Builder> builderProvider,
AssistantFeedbackController assistantFeedbackController,
- Optional<Bubbles> bubblesOptional,
+ Optional<BubblesManager> bubblesManagerOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback) {
mContext = context;
@@ -158,7 +158,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mBuilderProvider = builderProvider;
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
- mBubblesOptional = bubblesOptional;
+ mBubblesManagerOptional = bubblesManagerOptional;
mUiEventLogger = uiEventLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
}
@@ -491,7 +491,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mMainHandler,
mBgHandler,
onConversationSettingsListener,
- mBubblesOptional);
+ mBubblesManagerOptional);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e065b47ef5b1..f2ae3da73f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -306,7 +306,8 @@ public class NotificationIconAreaController implements
|| !entry.isPulseSuppressed())) {
return false;
}
- if (mBubblesOptional.isPresent() && mBubblesOptional.get().isBubbleExpanded(entry)) {
+ if (mBubblesOptional.isPresent()
+ && mBubblesOptional.get().isBubbleExpanded(entry.getKey())) {
return false;
}
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 9bd98f2e8c24..a8d41046a1eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -144,7 +144,6 @@ import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
@@ -229,6 +228,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
@@ -648,8 +648,9 @@ public class StatusBar extends SystemUI implements DemoMode,
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
+ private final Optional<BubblesManager> mBubblesManagerOptional;
private final Optional<Bubbles> mBubblesOptional;
- private final BubbleController.BubbleExpandListener mBubbleExpandListener;
+ private final Bubbles.BubbleExpandListener mBubbleExpandListener;
private ActivityIntentHelper mActivityIntentHelper;
private NotificationStackScrollLayoutController mStackScrollerController;
@@ -697,6 +698,7 @@ public class StatusBar extends SystemUI implements DemoMode,
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
VibratorHelper vibratorHelper,
+ Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -776,6 +778,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mVibratorHelper = vibratorHelper;
+ mBubblesManagerOptional = bubblesManagerOptional;
mBubblesOptional = bubblesOptional;
mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
@@ -1141,8 +1144,8 @@ public class StatusBar extends SystemUI implements DemoMode,
ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
- ScrimView scrimForBubble = mBubblesOptional.isPresent()
- ? mBubblesOptional.get().getScrimForBubble() : null;
+ ScrimView scrimForBubble = mBubblesManagerOptional.isPresent()
+ ? mBubblesManagerOptional.get().getScrimForBubble() : null;
mScrimController.setScrimVisibleListener(scrimsVisible -> {
mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 014d5c274c1f..acca953629c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -48,7 +48,6 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.EventLogTags;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -76,6 +75,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -104,7 +104,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
private final IDreamManager mDreamManager;
- private final Optional<Bubbles> mBubblesOptional;
+ private final Optional<BubblesManager> mBubblesManagerOptional;
private final Lazy<AssistManager> mAssistManagerLazy;
private final NotificationRemoteInputManager mRemoteInputManager;
private final GroupMembershipManager mGroupMembershipManager;
@@ -142,7 +142,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
IDreamManager dreamManager,
- Optional<Bubbles> bubblesOptional,
+ Optional<BubblesManager> bubblesManagerOptional,
Lazy<AssistManager> assistManagerLazy,
NotificationRemoteInputManager remoteInputManager,
GroupMembershipManager groupMembershipManager,
@@ -176,7 +176,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
mDreamManager = dreamManager;
- mBubblesOptional = bubblesOptional;
+ mBubblesManagerOptional = bubblesManagerOptional;
mAssistManagerLazy = assistManagerLazy;
mRemoteInputManager = remoteInputManager;
mGroupMembershipManager = groupMembershipManager;
@@ -399,14 +399,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
}
private void expandBubbleStackOnMainThread(NotificationEntry entry) {
- if (!mBubblesOptional.isPresent()) {
+ if (!mBubblesManagerOptional.isPresent()) {
return;
}
if (Looper.getMainLooper().isCurrentThread()) {
- mBubblesOptional.get().expandStackAndSelectBubble(entry);
+ mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
} else {
- mMainThreadHandler.post(() -> mBubblesOptional.get().expandStackAndSelectBubble(entry));
+ mMainThreadHandler.post(
+ () -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry));
}
}
@@ -606,7 +607,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
private final IDreamManager mDreamManager;
- private final Optional<Bubbles> mBubblesOptional;
+ private final Optional<BubblesManager> mBubblesManagerOptional;
private final Lazy<AssistManager> mAssistManagerLazy;
private final NotificationRemoteInputManager mRemoteInputManager;
private final GroupMembershipManager mGroupMembershipManager;
@@ -643,7 +644,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
IDreamManager dreamManager,
- Optional<Bubbles> bubblesOptional,
+ Optional<BubblesManager> bubblesManager,
Lazy<AssistManager> assistManagerLazy,
NotificationRemoteInputManager remoteInputManager,
GroupMembershipManager groupMembershipManager,
@@ -673,7 +674,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
mDreamManager = dreamManager;
- mBubblesOptional = bubblesOptional;
+ mBubblesManagerOptional = bubblesManager;
mAssistManagerLazy = assistManagerLazy;
mRemoteInputManager = remoteInputManager;
mGroupMembershipManager = groupMembershipManager;
@@ -729,7 +730,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mStatusBarKeyguardViewManager,
mKeyguardManager,
mDreamManager,
- mBubblesOptional,
+ mBubblesManagerOptional,
mAssistManagerLazy,
mRemoteInputManager,
mGroupMembershipManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 6d4099b656cb..b69da859b3c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -97,6 +97,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.util.Optional;
@@ -155,6 +156,7 @@ public interface StatusBarPhoneModule {
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
VibratorHelper vibratorHelper,
+ Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -233,6 +235,7 @@ public interface StatusBarPhoneModule {
wakefulnessLifecycle,
statusBarStateController,
vibratorHelper,
+ bubblesManagerOptional,
bubblesOptional,
visualStabilityManager,
deviceProvisionedController,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
new file mode 100644
index 000000000000..ad596c27ba97
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -0,0 +1,725 @@
+/*
+ * 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.wmshell;
+
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
+import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.ZenModeConfig;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.Dumpable;
+import com.android.systemui.bubbles.BubbleEntry;
+import com.android.systemui.bubbles.Bubbles;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.notification.NotificationChannelHelper;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.IntConsumer;
+
+/**
+ * The SysUi side bubbles manager which communicate with other SysUi components.
+ */
+@SysUISingleton
+public class BubblesManager implements Dumpable {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "BubblesManager" : TAG_BUBBLES;
+
+ private final Context mContext;
+ private final Bubbles mBubbles;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final ShadeController mShadeController;
+ private final IStatusBarService mBarService;
+ private final INotificationManager mNotificationManager;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final NotificationGroupManagerLegacy mNotificationGroupManager;
+ private final NotificationEntryManager mNotificationEntryManager;
+ private final NotifPipeline mNotifPipeline;
+
+ private final ScrimView mBubbleScrim;
+ private final Bubbles.SysuiProxy mSysuiProxy;
+ // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
+ private final List<NotifCallback> mCallbacks = new ArrayList<>();
+
+ /**
+ * Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present
+ * which means bubbles feature not support.
+ */
+ @Nullable
+ public static BubblesManager create(Context context,
+ Optional<Bubbles> bubblesOptional,
+ NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarStateController statusBarStateController,
+ ShadeController shadeController,
+ ConfigurationController configurationController,
+ @Nullable IStatusBarService statusBarService,
+ INotificationManager notificationManager,
+ NotificationInterruptStateProvider interruptionStateProvider,
+ ZenModeController zenModeController,
+ NotificationLockscreenUserManager notifUserManager,
+ NotificationGroupManagerLegacy groupManager,
+ NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
+ SysUiState sysUiState,
+ FeatureFlags featureFlags,
+ DumpManager dumpManager) {
+ if (bubblesOptional.isPresent()) {
+ return new BubblesManager(context, bubblesOptional.get(),
+ notificationShadeWindowController, statusBarStateController, shadeController,
+ configurationController, statusBarService, notificationManager,
+ interruptionStateProvider, zenModeController, notifUserManager,
+ groupManager, entryManager, notifPipeline, sysUiState, featureFlags,
+ dumpManager);
+ } else {
+ return null;
+ }
+ }
+
+ @VisibleForTesting
+ BubblesManager(Context context,
+ Bubbles bubbles,
+ NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarStateController statusBarStateController,
+ ShadeController shadeController,
+ ConfigurationController configurationController,
+ @Nullable IStatusBarService statusBarService,
+ INotificationManager notificationManager,
+ NotificationInterruptStateProvider interruptionStateProvider,
+ ZenModeController zenModeController,
+ NotificationLockscreenUserManager notifUserManager,
+ NotificationGroupManagerLegacy groupManager,
+ NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
+ SysUiState sysUiState,
+ FeatureFlags featureFlags,
+ DumpManager dumpManager) {
+ mContext = context;
+ mBubbles = bubbles;
+ mNotificationShadeWindowController = notificationShadeWindowController;
+ mShadeController = shadeController;
+ mNotificationManager = notificationManager;
+ mNotificationInterruptStateProvider = interruptionStateProvider;
+ mNotificationGroupManager = groupManager;
+ mNotificationEntryManager = entryManager;
+ mNotifPipeline = notifPipeline;
+
+ mBarService = statusBarService == null
+ ? IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE))
+ : statusBarService;
+
+ mBubbleScrim = new ScrimView(mContext);
+ mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mBubbles.setBubbleScrim(mBubbleScrim);
+
+ if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
+ setupNotifPipeline();
+ } else {
+ setupNEM();
+ }
+
+ dumpManager.registerDumpable(TAG, this);
+
+ statusBarStateController.addCallback(new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ boolean isShade = newState == SHADE;
+ bubbles.onStatusBarStateChanged(isShade);
+ }
+ });
+
+ configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mBubbles.onConfigChanged(newConfig);
+ }
+
+ @Override
+ public void onUiModeChanged() {
+ mBubbles.updateForThemeChanges();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ mBubbles.updateForThemeChanges();
+ }
+ });
+
+ zenModeController.addCallback(new ZenModeController.Callback() {
+ @Override
+ public void onZenChanged(int zen) {
+ mBubbles.onZenStateChanged();
+ }
+
+ @Override
+ public void onConfigChanged(ZenModeConfig config) {
+ mBubbles.onZenStateChanged();
+ }
+ });
+
+ notifUserManager.addUserChangedListener(
+ new NotificationLockscreenUserManager.UserChangedListener() {
+ @Override
+ public void onUserChanged(int userId) {
+ mBubbles.onUserChanged(userId);
+ }
+ });
+
+ mSysuiProxy = new Bubbles.SysuiProxy() {
+ @Override
+ @Nullable
+ public BubbleEntry getPendingOrActiveEntry(String key) {
+ NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
+ return entry == null ? null : notifToBubbleEntry(entry);
+ }
+
+ @Override
+ public List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys) {
+ List<BubbleEntry> result = new ArrayList<>();
+ List<NotificationEntry> activeEntries =
+ mNotificationEntryManager.getActiveNotificationsForCurrentUser();
+ for (int i = 0; i < activeEntries.size(); i++) {
+ NotificationEntry entry = activeEntries.get(i);
+ if (savedBubbleKeys.contains(entry.getKey())
+ && mNotificationInterruptStateProvider.shouldBubbleUp(entry)
+ && entry.isBubble()) {
+ result.add(notifToBubbleEntry(entry));
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean isNotificationShadeExpand() {
+ return mNotificationShadeWindowController.getPanelExpanded();
+ }
+
+ @Override
+ public boolean shouldBubbleUp(String key) {
+ final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
+ key);
+ if (entry != null) {
+ return mNotificationInterruptStateProvider.shouldBubbleUp(entry);
+ }
+ return false;
+ }
+
+ @Override
+ public void setNotificationInterruption(String key) {
+ final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
+ key);
+ if (entry != null && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
+ entry.setInterruption();
+ }
+ }
+
+ @Override
+ public void requestNotificationShadeTopUi(boolean requestTopUi, String componentTag) {
+ mNotificationShadeWindowController.setRequestTopUi(requestTopUi, componentTag);
+ }
+
+ @Override
+ public void notifyRemoveNotification(String key, int reason) {
+ final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
+ key);
+ if (entry != null) {
+ for (NotifCallback cb : mCallbacks) {
+ cb.removeNotification(entry, getDismissedByUserStats(entry, true), reason);
+ }
+ }
+ }
+
+ @Override
+ public void notifyInvalidateNotifications(String reason) {
+ for (NotifCallback cb : mCallbacks) {
+ cb.invalidateNotifications(reason);
+ }
+ }
+
+ @Override
+ public void notifyMaybeCancelSummary(String key) {
+ final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
+ key);
+ if (entry != null) {
+ for (NotifCallback cb : mCallbacks) {
+ cb.maybeCancelSummary(entry);
+ }
+ }
+ }
+
+ @Override
+ public void removeNotificationEntry(String key) {
+ final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
+ key);
+ if (entry != null) {
+ mNotificationGroupManager.onEntryRemoved(entry);
+ }
+ }
+
+ @Override
+ public void updateNotificationBubbleButton(String key) {
+ final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
+ key);
+ if (entry != null && entry.getRow() != null) {
+ entry.getRow().updateBubbleButton();
+ }
+ }
+
+ @Override
+ public void updateNotificationSuppression(String key) {
+ final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
+ key);
+ if (entry != null) {
+ mNotificationGroupManager.updateSuppression(entry);
+ }
+ }
+
+ @Override
+ public void onStackExpandChanged(boolean shouldExpand) {
+ sysUiState
+ .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+ .commitUpdate(mContext.getDisplayId());
+ }
+
+ @Override
+ public void onUnbubbleConversation(String key) {
+ final NotificationEntry entry =
+ mNotificationEntryManager.getPendingOrActiveNotif(key);
+ if (entry != null) {
+ onUserChangedBubble(entry, false /* shouldBubble */);
+ }
+ }
+ };
+ mBubbles.setSysuiProxy(mSysuiProxy);
+ }
+
+ private void setupNEM() {
+ mNotificationEntryManager.addNotificationEntryListener(
+ new NotificationEntryListener() {
+ @Override
+ public void onPendingEntryAdded(NotificationEntry entry) {
+ BubblesManager.this.onEntryAdded(entry);
+ }
+
+ @Override
+ public void onPreEntryUpdated(NotificationEntry entry) {
+ BubblesManager.this.onEntryUpdated(entry);
+ }
+
+ @Override
+ public void onEntryRemoved(NotificationEntry entry,
+ @Nullable NotificationVisibility visibility,
+ boolean removedByUser, int reason) {
+ BubblesManager.this.onEntryRemoved(entry);
+ }
+
+ @Override
+ public void onNotificationRankingUpdated(RankingMap rankingMap) {
+ BubblesManager.this.onRankingUpdate(rankingMap);
+ }
+ });
+
+ // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
+ mNotificationEntryManager.addNotificationRemoveInterceptor(
+ (key, entry, dismissReason) -> {
+ final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
+ final boolean isUserDismiss = dismissReason == REASON_CANCEL
+ || dismissReason == REASON_CLICK;
+ final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
+ || dismissReason == REASON_APP_CANCEL_ALL;
+ final boolean isSummaryCancel =
+ dismissReason == REASON_GROUP_SUMMARY_CANCELED;
+
+ // Need to check for !appCancel here because the notification may have
+ // previously been dismissed & entry.isRowDismissed would still be true
+ boolean userRemovedNotif =
+ (entry != null && entry.isRowDismissed() && !isAppCancel)
+ || isClearAll || isUserDismiss || isSummaryCancel;
+
+ if (userRemovedNotif) {
+ return handleDismissalInterception(entry);
+ }
+ return false;
+ });
+
+ mNotificationGroupManager.registerGroupChangeListener(
+ new NotificationGroupManagerLegacy.OnGroupChangeListener() {
+ @Override
+ public void onGroupSuppressionChanged(
+ NotificationGroupManagerLegacy.NotificationGroup group,
+ boolean suppressed) {
+ // More notifications could be added causing summary to no longer
+ // be suppressed -- in this case need to remove the key.
+ final String groupKey = group.summary != null
+ ? group.summary.getSbn().getGroupKey()
+ : null;
+ if (!suppressed && groupKey != null
+ && mBubbles.isSummarySuppressed(groupKey)) {
+ mBubbles.removeSuppressedSummary(groupKey);
+ }
+ }
+ });
+
+ addNotifCallback(new NotifCallback() {
+ @Override
+ public void removeNotification(NotificationEntry entry,
+ DismissedByUserStats dismissedByUserStats, int reason) {
+ mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
+ dismissedByUserStats, reason);
+ }
+
+ @Override
+ public void invalidateNotifications(String reason) {
+ mNotificationEntryManager.updateNotifications(reason);
+ }
+
+ @Override
+ public void maybeCancelSummary(NotificationEntry entry) {
+ // Check if removed bubble has an associated suppressed group summary that needs
+ // to be removed now.
+ final String groupKey = entry.getSbn().getGroupKey();
+ if (mBubbles.isSummarySuppressed(groupKey)) {
+ mBubbles.removeSuppressedSummary(groupKey);
+
+ final NotificationEntry summary =
+ mNotificationEntryManager.getActiveNotificationUnfiltered(
+ mBubbles.getSummaryKey(groupKey));
+ if (summary != null) {
+ mNotificationEntryManager.performRemoveNotification(
+ summary.getSbn(),
+ getDismissedByUserStats(summary, false),
+ UNDEFINED_DISMISS_REASON);
+ }
+ }
+
+ // Check if we still need to remove the summary from NoManGroup because the summary
+ // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
+ // For example:
+ // 1. Bubbled notifications (group) is posted to shade and are visible bubbles
+ // 2. User expands bubbles so now their respective notifications in the shade are
+ // hidden, including the group summary
+ // 3. User removes all bubbles
+ // 4. We expect all the removed bubbles AND the summary (note: the summary was
+ // never added to the suppressedSummary list in BubbleData, so we add this check)
+ NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry);
+ if (summary != null) {
+ ArrayList<NotificationEntry> summaryChildren =
+ mNotificationGroupManager.getLogicalChildren(summary.getSbn());
+ boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
+ if (!isSummaryThisNotif && (summaryChildren == null
+ || summaryChildren.isEmpty())) {
+ mNotificationEntryManager.performRemoveNotification(
+ summary.getSbn(),
+ getDismissedByUserStats(summary, false),
+ UNDEFINED_DISMISS_REASON);
+ }
+ }
+ }
+ });
+ }
+
+ private void setupNotifPipeline() {
+ mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ BubblesManager.this.onEntryAdded(entry);
+ }
+
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ BubblesManager.this.onEntryUpdated(entry);
+ }
+
+ @Override
+ public void onEntryRemoved(NotificationEntry entry,
+ @NotifCollection.CancellationReason int reason) {
+ BubblesManager.this.onEntryRemoved(entry);
+ }
+
+ @Override
+ public void onRankingUpdate(RankingMap rankingMap) {
+ BubblesManager.this.onRankingUpdate(rankingMap);
+ }
+ });
+ }
+
+ void onEntryAdded(NotificationEntry entry) {
+ if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
+ && entry.isBubble()) {
+ mBubbles.onEntryAdded(notifToBubbleEntry(entry));
+ }
+ }
+
+ void onEntryUpdated(NotificationEntry entry) {
+ mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
+ mNotificationInterruptStateProvider.shouldBubbleUp(entry));
+ }
+
+ void onEntryRemoved(NotificationEntry entry) {
+ mBubbles.onEntryRemoved(notifToBubbleEntry(entry));
+ }
+
+ void onRankingUpdate(RankingMap rankingMap) {
+ mBubbles.onRankingUpdated(rankingMap);
+ }
+
+ /**
+ * Gets the DismissedByUserStats used by {@link NotificationEntryManager}.
+ * Will not be necessary when using the new notification pipeline's {@link NotifCollection}.
+ * Instead, this is taken care of by {@link BubbleCoordinator}.
+ */
+ private DismissedByUserStats getDismissedByUserStats(
+ NotificationEntry entry,
+ boolean isVisible) {
+ return new DismissedByUserStats(
+ DISMISSAL_BUBBLE,
+ DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(
+ entry.getKey(),
+ entry.getRanking().getRank(),
+ mNotificationEntryManager.getActiveNotificationsCount(),
+ isVisible,
+ NotificationLogger.getNotificationLocation(entry)));
+ }
+
+ /**
+ * Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController}
+ * since we want the scrim's appearance and behavior to be identical to that of the notification
+ * shade scrim.
+ */
+ public ScrimView getScrimForBubble() {
+ return mBubbleScrim;
+ }
+
+ /**
+ * We intercept notification entries (including group summaries) dismissed by the user when
+ * there is an active bubble associated with it. We do this so that developers can still
+ * cancel it (and hence the bubbles associated with it).
+ *
+ * @return true if we want to intercept the dismissal of the entry, else false.
+ * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer)
+ */
+ public boolean handleDismissalInterception(NotificationEntry entry) {
+ if (entry == null) {
+ return false;
+ }
+
+ List<NotificationEntry> children = entry.getAttachedNotifChildren();
+ List<BubbleEntry> bubbleChildren = null;
+ if (children != null) {
+ bubbleChildren = new ArrayList<>();
+ for (int i = 0; i < children.size(); i++) {
+ bubbleChildren.add(notifToBubbleEntry(children.get(i)));
+ }
+ }
+
+ return mBubbles.handleDismissalInterception(notifToBubbleEntry(entry), bubbleChildren,
+ // TODO : b/171847985 should re-work on notification side to make this more clear.
+ (int i) -> {
+ if (i >= 0) {
+ for (NotifCallback cb : mCallbacks) {
+ cb.removeNotification(children.get(i),
+ getDismissedByUserStats(children.get(i), true),
+ REASON_GROUP_SUMMARY_CANCELED);
+ }
+ } else {
+ mNotificationGroupManager.onEntryRemoved(entry);
+ }
+ });
+ }
+
+ /**
+ * Request the stack expand if needed, then select the specified Bubble as current.
+ * If no bubble exists for this entry, one is created.
+ *
+ * @param entry the notification for the bubble to be selected
+ */
+ public void expandStackAndSelectBubble(NotificationEntry entry) {
+ mBubbles.expandStackAndSelectBubble(notifToBubbleEntry(entry));
+ }
+
+ /** See {@link NotifCallback}. */
+ public void addNotifCallback(NotifCallback callback) {
+ mCallbacks.add(callback);
+ }
+
+ /**
+ * When a notification is marked Priority, expand the stack if needed,
+ * then (maybe create and) select the given bubble.
+ *
+ * @param entry the notification for the bubble to show
+ */
+ public void onUserChangedImportance(NotificationEntry entry) {
+ try {
+ int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
+ mBarService.onNotificationBubbleChanged(entry.getKey(), true, flags);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ mShadeController.collapsePanel(true);
+ if (entry.getRow() != null) {
+ entry.getRow().updateBubbleButton();
+ }
+ }
+
+ /**
+ * Called when a user has indicated that an active notification should be shown as a bubble.
+ * <p>
+ * This method will collapse the shade, create the bubble without a flyout or dot, and suppress
+ * the notification from appearing in the shade.
+ *
+ * @param entry the notification to change bubble state for.
+ * @param shouldBubble whether the notification should show as a bubble or not.
+ */
+ public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) {
+ NotificationChannel channel = entry.getChannel();
+ final String appPkg = entry.getSbn().getPackageName();
+ final int appUid = entry.getSbn().getUid();
+ if (channel == null || appPkg == null) {
+ return;
+ }
+
+ // Update the state in NotificationManagerService
+ try {
+ int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ flags |= Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE;
+ mBarService.onNotificationBubbleChanged(entry.getKey(), shouldBubble, flags);
+ } catch (RemoteException e) {
+ }
+
+ // Change the settings
+ channel = NotificationChannelHelper.createConversationChannelIfNeeded(mContext,
+ mNotificationManager, entry, channel);
+ channel.setAllowBubbles(shouldBubble);
+ try {
+ int currentPref = mNotificationManager.getBubblePreferenceForPackage(appPkg, appUid);
+ if (shouldBubble && currentPref == BUBBLE_PREFERENCE_NONE) {
+ mNotificationManager.setBubblesAllowed(appPkg, appUid, BUBBLE_PREFERENCE_SELECTED);
+ }
+ mNotificationManager.updateNotificationChannelForPackage(appPkg, appUid, channel);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+
+ if (shouldBubble) {
+ mShadeController.collapsePanel(true);
+ if (entry.getRow() != null) {
+ entry.getRow().updateBubbleButton();
+ }
+ }
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ mBubbles.dump(fd, pw, args);
+ }
+
+ static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
+ return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
+ e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
+ e.shouldSuppressPeek());
+ }
+
+ /**
+ * Callback for when the BubbleController wants to interact with the notification pipeline to:
+ * - Remove a previously bubbled notification
+ * - Update the notification shade since bubbled notification should/shouldn't be showing
+ */
+ public interface NotifCallback {
+ /**
+ * Called when a bubbled notification that was hidden from the shade is now being removed
+ * This can happen when an app cancels a bubbled notification or when the user dismisses a
+ * bubble.
+ */
+ void removeNotification(@NonNull NotificationEntry entry,
+ @NonNull DismissedByUserStats stats, int reason);
+
+ /**
+ * Called when a bubbled notification has changed whether it should be
+ * filtered from the shade.
+ */
+ void invalidateNotifications(@NonNull String reason);
+
+ /**
+ * Called on a bubbled entry that has been removed when there are no longer
+ * bubbled entries in its group.
+ *
+ * Checks whether its group has any other (non-bubbled) children. If it doesn't,
+ * removes all remnants of the group's summary from the notification pipeline.
+ * TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
+ */
+ void maybeCancelSummary(@NonNull NotificationEntry entry);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 6bedd392ef3a..b7efa7c4c5ac 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -17,8 +17,6 @@
package com.android.systemui.wmshell;
import android.content.Context;
-import android.os.Handler;
-import android.view.LayoutInflater;
import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -27,6 +25,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipUiEventLogger;
@@ -53,6 +52,8 @@ public abstract class TvPipModule {
PipBoundsState pipBoundsState,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
+ PipMediaController pipMediaController,
+ PipNotification pipNotification,
WindowManagerShellWrapper windowManagerShellWrapper) {
return Optional.of(
new PipController(
@@ -60,16 +61,16 @@ public abstract class TvPipModule {
pipBoundsState,
pipBoundsHandler,
pipTaskOrganizer,
+ pipMediaController,
+ pipNotification,
windowManagerShellWrapper));
}
@WMSingleton
@Provides
static PipControlsViewController providePipControlsViewController(
- PipControlsView pipControlsView, PipController pipController,
- LayoutInflater layoutInflater, Handler handler) {
- return new PipControlsViewController(pipControlsView, pipController, layoutInflater,
- handler);
+ PipControlsView pipControlsView, PipController pipController) {
+ return new PipControlsViewController(pipControlsView, pipController);
}
@WMSingleton
@@ -81,8 +82,8 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
static PipNotification providePipNotification(Context context,
- PipController pipController) {
- return new PipNotification(context, pipController);
+ PipMediaController pipMediaController) {
+ return new PipNotification(context, pipMediaController);
}
@WMSingleton
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 81f448afa13f..91ae08e07677 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -34,6 +34,7 @@ import com.android.wm.shell.common.AnimationThread;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.HandlerExecutor;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
@@ -157,9 +158,9 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue,
- @Main Handler handler, TransactionPool transactionPool) {
+ ShellExecutor mainExecutor, TransactionPool transactionPool) {
return new ShellTaskOrganizer(syncQueue, transactionPool,
- new HandlerExecutor(handler), AnimationThread.instance().getExecutor());
+ mainExecutor, AnimationThread.instance().getExecutor());
}
@BindsOptionalOf
@@ -174,4 +175,11 @@ public abstract class WMShellBaseModule {
DisplayController displayController) {
return Optional.ofNullable(OneHandedController.create(context, displayController));
}
+
+ @WMSingleton
+ @Provides
+ static ShellExecutor provideMainShellExecutor(@Main Handler handler) {
+ return new HandlerExecutor(handler);
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index f376df06badd..b6fbd589ae2c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -27,6 +27,7 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
@@ -82,11 +83,12 @@ public class WMShellModule {
PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer,
- PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper) {
+ PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
+ ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(context, displayController,
pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController,
pipMenuActivityController, pipTaskOrganizer, pipTouchHandler,
- windowManagerShellWrapper));
+ windowManagerShellWrapper, mainExecutor));
}
@WMSingleton
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index e73ed801b7df..e6479ddaedb3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -439,7 +439,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt());
- verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt(), any());
+ verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
@Test
@@ -466,7 +466,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt());
- verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt(), any());
+ verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index e24f4ca3581d..a95396cccd66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -132,7 +132,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
- assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mUdfpsSensorId);
+ assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mSensorProps.sensorId);
}
private void setUpResources() {
@@ -222,8 +222,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
// THEN the event is passed to the FingerprintManager
- verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
- eq(0f), eq(0f));
+ verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0),
+ eq(0), eq(0f), eq(0f));
// AND the scrim and dot is shown
verify(mUdfpsView).showScrimAndDot();
}
@@ -236,8 +236,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0);
// THEN the event is passed to the FingerprintManager
- verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
- anyFloat(), anyFloat());
+ verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0),
+ eq(0), anyFloat(), anyFloat());
// AND the scrim and dot is shown
verify(mUdfpsView).showScrimAndDot();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 0c872db45194..31c08ae471ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -102,10 +102,10 @@ public class BubbleDataTest extends SysuiTestCase {
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@Mock
- private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+ private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
@Mock
- private BubbleController.PendingIntentCanceledListener mPendingIntentCanceledListener;
+ private Bubbles.PendingIntentCanceledListener mPendingIntentCanceledListener;
@Before
public void setUp() throws Exception {
@@ -171,12 +171,11 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
// Verify
verifyUpdateReceived();
- assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_USER_GESTURE);
+ assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_USER_GESTURE);
}
@Test
@@ -269,7 +268,7 @@ public class BubbleDataTest extends SysuiTestCase {
sendUpdatedEntryAtTime(mEntryC1, 6000);
verifyUpdateReceived();
- assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED);
+ assertBubbleRemoved(mBubbleA1, Bubbles.DISMISS_AGED);
assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
Bubble bubbleA1 = mBubbleData.getOrCreateBubble(mEntryA1, null /* persistedBubble */);
@@ -277,7 +276,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.notificationEntryUpdated(bubbleA1, false /* suppressFlyout*/,
true /* showInShade */);
verifyUpdateReceived();
- assertBubbleRemoved(mBubbleA2, BubbleController.DISMISS_AGED);
+ assertBubbleRemoved(mBubbleA2, Bubbles.DISMISS_AGED);
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
@@ -294,14 +293,12 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
mBubbleData.setMaxOverflowBubbles(1);
- mBubbleData.dismissBubbleWithKey(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
// Overflow max of 1 is reached; A1 is oldest, so it gets removed
- mBubbleData.dismissBubbleWithKey(
- mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
@@ -322,14 +319,12 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(),
- BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
// Test
- mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(),
- BubbleController.DISMISS_GROUP_CANCELLED);
+ mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_GROUP_CANCELLED);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of());
}
@@ -409,8 +404,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(
- mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
// TODO: this should fail if things work as I expect them to?
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1);
@@ -430,8 +424,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderNotChanged();
}
@@ -450,8 +443,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(
- mEntryA2.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB2);
}
@@ -545,8 +537,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
// Verify the selection was cleared.
verifyUpdateReceived();
@@ -646,8 +637,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(
- mEntryB2.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryB2.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderChangedTo(mBubbleA2, mBubbleB1, mBubbleA1);
}
@@ -671,13 +661,11 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(
- mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryA2.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB1);
- mBubbleData.dismissBubbleWithKey(
- mEntryB1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryB1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleA1);
}
@@ -791,8 +779,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.dismissBubbleWithKey(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertExpandedChangedTo(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index 29ead593c51a..690a1ad42861 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -60,7 +60,7 @@ public class BubbleTest extends SysuiTestCase {
private Bubble mBubble;
@Mock
- private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+ private Bubbles.NotificationSuppressionChangedListener mSuppressionListener;
@Before
public void setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
deleted file mode 100644
index aaeee16dc1fd..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.app.INotificationManager;
-import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.os.Handler;
-import android.view.WindowManager;
-
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.common.FloatingContentCoordinator;
-
-/**
- * Testable BubbleController subclass that immediately synchronizes surfaces.
- */
-public class TestableBubbleController extends BubbleController {
-
- // Let's assume surfaces can be synchronized immediately.
- TestableBubbleController(Context context,
- NotificationShadeWindowController notificationShadeWindowController,
- StatusBarStateController statusBarStateController,
- ShadeController shadeController,
- BubbleData data,
- ConfigurationController configurationController,
- NotificationInterruptStateProvider interruptionStateProvider,
- ZenModeController zenModeController,
- NotificationLockscreenUserManager lockscreenUserManager,
- NotificationGroupManagerLegacy groupManager,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- FeatureFlags featureFlags,
- DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator,
- BubbleDataRepository dataRepository,
- SysUiState sysUiState,
- INotificationManager notificationManager,
- IStatusBarService statusBarService,
- WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps,
- BubbleLogger bubbleLogger,
- Handler mainHandler,
- ShellTaskOrganizer shellTaskOrganizer,
- BubblePositioner positioner) {
- super(context,
- notificationShadeWindowController, statusBarStateController, shadeController,
- data, Runnable::run, configurationController, interruptionStateProvider,
- zenModeController, lockscreenUserManager, groupManager, entryManager,
- notifPipeline, featureFlags, dumpManager, floatingContentCoordinator,
- dataRepository, sysUiState, notificationManager, statusBarService,
- windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger,
- mainHandler, shellTaskOrganizer, positioner);
- setInflateSynchronously(true);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 6ceac131ed4b..0d352c1b42d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -447,6 +447,25 @@ public class MediaOutputControllerTest extends SysuiTestCase {
}
@Test
+ public void getNotificationLargeIcon_withoutLargeIcon_returnsNull() {
+ final List<NotificationEntry> entryList = new ArrayList<>();
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ final StatusBarNotification sbn = mock(StatusBarNotification.class);
+ final Notification notification = mock(Notification.class);
+ entryList.add(entry);
+
+ when(mNotificationEntryManager.getActiveNotificationsForCurrentUser())
+ .thenReturn(entryList);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(notification);
+ when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(notification.hasMediaSession()).thenReturn(true);
+ when(notification.getLargeIcon()).thenReturn(null);
+
+ assertThat(mMediaOutputController.getNotificationIcon()).isNull();
+ }
+
+ @Test
public void getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat() {
final List<NotificationEntry> entryList = new ArrayList<>();
final NotificationEntry entry = mock(NotificationEntry.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index aeb625c98283..152c51e1f9f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -83,7 +83,7 @@ public class RankingBuilder {
mCanBubble = ranking.canBubble();
mIsVisuallyInterruptive = ranking.visuallyInterruptive();
mIsConversation = ranking.isConversation();
- mShortcutInfo = ranking.getShortcutInfo();
+ mShortcutInfo = ranking.getConversationShortcutInfo();
mRankingAdjustment = ranking.getRankingAdjustment();
mIsBubble = ranking.isBubble();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index d835123e4cad..cd46dda772e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -116,7 +116,7 @@ public class NotificationFilterTest extends SysuiTestCase {
new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
() -> mock(PeopleNotificationIdentifier.class),
- Optional.of(() -> mock(Bubbles.class))));
+ Optional.of(mock(Bubbles.class))));
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 61edcf9c200c..4698b8e50efb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -76,12 +76,12 @@ import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
import org.junit.Rule;
@@ -137,7 +137,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
@Mock
private OnUserInteractionCallback mOnUserInteractionCallback;
@Mock
- private Bubbles mBubbles;
+ private BubblesManager mBubblesManager;
@Mock
private LauncherApps mLauncherApps;
@Mock
@@ -255,7 +255,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
assertEquals(mIconDrawable, view.getDrawable());
}
@@ -279,7 +279,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -330,7 +330,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertTrue(textView.getText().toString().contains(group.getName()));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -355,7 +355,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
assertEquals(GONE, textView.getVisibility());
@@ -379,7 +379,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
}
@@ -414,7 +414,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -442,7 +442,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -468,7 +468,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -495,7 +495,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
false,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -520,7 +520,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View view = mNotificationInfo.findViewById(R.id.silence);
assertThat(view.isSelected()).isTrue();
}
@@ -548,7 +548,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
@@ -579,7 +579,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo(
@@ -609,7 +609,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -653,7 +653,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mTestableLooper.processAllMessages();
@@ -696,7 +696,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -740,7 +740,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -777,7 +777,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -813,7 +813,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -851,7 +851,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -887,7 +887,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -923,7 +923,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -958,7 +958,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
View silence = mNotificationInfo.findViewById(R.id.silence);
silence.performClick();
@@ -992,7 +992,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
anyString(), anyInt(), any(), eq(CONVERSATION_ID));
@@ -1017,7 +1017,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mBuilderProvider,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
anyString(), anyInt(), any(), eq(CONVERSATION_ID));
@@ -1052,7 +1052,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
() -> b,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
// WHEN user clicks "priority"
mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
@@ -1092,7 +1092,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
() -> b,
true,
mTestHandler,
- mTestHandler, null, Optional.of(mBubbles));
+ mTestHandler, null, Optional.of(mBubblesManager));
// WHEN user clicks "priority"
mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 8a5afe6ce667..dbaf5c467c45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -84,6 +84,7 @@ import com.android.systemui.statusbar.policy.InflatedSmartReplies;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.wmshell.BubblesManager;
import org.junit.After;
import org.junit.Before;
@@ -96,6 +97,7 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
+import java.util.Optional;
import java.util.concurrent.CountDownLatch;
/**
@@ -243,7 +245,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
true,
null,
mFalsingManager,
- mPeopleNotificationIdentifier
+ mPeopleNotificationIdentifier,
+ Optional.of(mock(BubblesManager.class))
));
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index bbc1df21237f..3000b8b449c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -67,7 +67,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -81,6 +80,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager.O
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
import org.junit.Ignore;
@@ -131,7 +131,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private ChannelEditorDialogController mChannelEditorDialogController;
@Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Mock private UserContextProvider mContextTracker;
- @Mock private Bubbles mBubbles;
+ @Mock private BubblesManager mBubblesManager;
@Mock(answer = Answers.RETURNS_SELF)
private PriorityOnboardingDialogController.Builder mBuilder;
private Provider<PriorityOnboardingDialogController.Builder> mProvider = () -> mBuilder;
@@ -156,7 +156,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
() -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider,
mINotificationManager, mLauncherApps, mShortcutManager,
mChannelEditorDialogController, mContextTracker, mProvider,
- mAssistantFeedbackController, Optional.of(mBubbles),
+ mAssistantFeedbackController, Optional.of(mBubblesManager),
new UiEventLoggerFake(), mOnUserInteractionCallback);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mCheckSaveListener, mOnSettingsClickListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 847e0a474a6a..48375e02f64f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -70,6 +70,7 @@ import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.InflatedSmartReplies;
+import com.android.systemui.wmshell.BubblesManager;
import org.mockito.ArgumentCaptor;
@@ -121,7 +122,7 @@ public class NotificationTestHelper {
mGroupMembershipManager = new NotificationGroupManagerLegacy(
mStatusBarStateController,
() -> mock(PeopleNotificationIdentifier.class),
- Optional.of(() -> mock(Bubbles.class)));
+ Optional.of((mock(Bubbles.class))));
mGroupExpansionManager = mGroupMembershipManager;
mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class),
@@ -430,7 +431,8 @@ public class NotificationTestHelper {
mock(FalsingManager.class),
mStatusBarStateController,
mPeopleNotificationIdentifier,
- mock(OnUserInteractionCallback.class));
+ mock(OnUserInteractionCallback.class),
+ Optional.of(mock(BubblesManager.class)));
row.setAboveShelfChangedListener(aboveShelf -> { });
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 7d84f86cc7b3..b0086ef1d4fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -92,7 +92,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
() -> mock(PeopleNotificationIdentifier.class),
- Optional.of(() -> mock(Bubbles.class)));
+ Optional.of(mock(Bubbles.class)));
mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 29e445a13e24..f81672ab6a37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -70,7 +70,7 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
mGroupManager = new NotificationGroupManagerLegacy(
mock(StatusBarStateController.class),
() -> mock(PeopleNotificationIdentifier.class),
- Optional.of(() -> mock(Bubbles.class)));
+ Optional.of(mock(Bubbles.class)));
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index f7489b1c164a..1f31fcd2a2bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -53,7 +53,6 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -77,6 +76,7 @@ import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
import org.junit.Test;
@@ -116,7 +116,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private Handler mHandler;
@Mock
- private Bubbles mBubbles;
+ private BubblesManager mBubblesManager;
@Mock
private ShadeControllerImpl mShadeController;
@Mock
@@ -193,7 +193,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mock(KeyguardManager.class),
mock(IDreamManager.class),
- Optional.of(mBubbles),
+ Optional.of(mBubblesManager),
() -> mAssistManager,
mRemoteInputManager,
mock(NotificationGroupManagerLegacy.class),
@@ -280,7 +280,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
- verify(mBubbles).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
+ verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
// This is called regardless, and simply short circuits when there is nothing to do.
verify(mShadeController, atLeastOnce()).collapsePanel();
@@ -312,7 +312,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
- verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
+ verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
verify(mShadeController, atLeastOnce()).collapsePanel();
@@ -342,7 +342,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
// Then
- verify(mBubbles).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
+ verify(mBubblesManager).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
verify(mShadeController, atLeastOnce()).collapsePanel();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d6a7acbcbd78..9f096dada6f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -145,6 +145,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
@@ -223,6 +224,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private UserSwitcherController mUserSwitcherController;
@Mock private NetworkController mNetworkController;
@Mock private VibratorHelper mVibratorHelper;
+ @Mock private BubblesManager mBubblesManager;
@Mock private Bubbles mBubbles;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@@ -377,6 +379,7 @@ public class StatusBarTest extends SysuiTestCase {
wakefulnessLifecycle,
mStatusBarStateController,
mVibratorHelper,
+ Optional.of(mBubblesManager),
Optional.of(mBubbles),
mVisualStabilityManager,
mDeviceProvisionedController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index d9e9a8b26f0f..88d04011e738 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.systemui.wmshell;
import static android.app.Notification.FLAG_BUBBLE;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
@@ -64,6 +64,14 @@ import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.Bubble;
+import com.android.systemui.bubbles.BubbleData;
+import com.android.systemui.bubbles.BubbleDataRepository;
+import com.android.systemui.bubbles.BubbleEntry;
+import com.android.systemui.bubbles.BubbleLogger;
+import com.android.systemui.bubbles.BubblePositioner;
+import com.android.systemui.bubbles.BubbleStackView;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -112,12 +120,12 @@ import java.util.List;
/**
* Tests the NotificationEntryManager setup with BubbleController.
* The {@link NotifPipeline} setup with BubbleController is tested in
- * {@link NewNotifPipelineBubbleControllerTest}.
+ * {@link NewNotifPipelineBubblesTest}.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class BubbleControllerTest extends SysuiTestCase {
+public class BubblesTest extends SysuiTestCase {
@Mock
private NotificationEntryManager mNotificationEntryManager;
@Mock
@@ -157,6 +165,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
+ private BubblesManager mBubblesManager;
private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotificationEntryListener mEntryListener;
@@ -167,9 +176,12 @@ public class BubbleControllerTest extends SysuiTestCase {
private ExpandableNotificationRow mRow2;
private ExpandableNotificationRow mRow3;
private ExpandableNotificationRow mNonBubbleNotifRow;
+ private BubbleEntry mBubbleEntry;
+ private BubbleEntry mBubbleEntry2;
+ private BubbleEntry mBubbleEntry3;
@Mock
- private BubbleController.BubbleExpandListener mBubbleExpandListener;
+ private Bubbles.BubbleExpandListener mBubbleExpandListener;
@Mock
private PendingIntent mDeleteIntent;
@Mock
@@ -226,6 +238,9 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
+ mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow.getEntry());
+ mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2.getEntry());
+ mBubbleEntry3 = BubblesManager.notifToBubbleEntry(mRow3.getEntry());
// Return non-null notification data from the NEM
when(mNotificationEntryManager
@@ -258,26 +273,13 @@ public class BubbleControllerTest extends SysuiTestCase {
mock(HeadsUpManager.class),
mock(Handler.class)
);
+
when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mBubbleController = new TestableBubbleController(
mContext,
- mNotificationShadeWindowController,
- mStatusBarStateController,
- mShadeController,
mBubbleData,
- mConfigurationController,
- interruptionStateProvider,
- mZenModeController,
- mLockscreenUserManager,
- mNotificationGroupManager,
- mNotificationEntryManager,
- mNotifPipeline,
- mFeatureFlagsOldPipeline,
- mDumpManager,
mFloatingContentCoordinator,
mDataRepository,
- mSysUiState,
- mock(INotificationManager.class),
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
@@ -288,6 +290,25 @@ public class BubbleControllerTest extends SysuiTestCase {
mPositioner);
mBubbleController.setExpandListener(mBubbleExpandListener);
+ mBubblesManager = new BubblesManager(
+ mContext,
+ mBubbleController,
+ mNotificationShadeWindowController,
+ mStatusBarStateController,
+ mShadeController,
+ mConfigurationController,
+ mStatusBarService,
+ mock(INotificationManager.class),
+ interruptionStateProvider,
+ mZenModeController,
+ mLockscreenUserManager,
+ mNotificationGroupManager,
+ mNotificationEntryManager,
+ mNotifPipeline,
+ mSysUiState,
+ mFeatureFlagsOldPipeline,
+ mDumpManager);
+
// Get a reference to the BubbleController's entry listener
verify(mNotificationEntryManager, atLeastOnce())
.addNotificationEntryListener(mEntryListenerCaptor.capture());
@@ -300,7 +321,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testAddBubble() {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mSysUiStateBubblesExpanded);
@@ -309,20 +330,20 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testHasBubbles() {
assertFalse(mBubbleController.hasBubbles());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mSysUiStateBubblesExpanded);
}
@Test
public void testRemoveBubble() {
- mBubbleController.updateBubble(mRow.getEntry());
- assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
+ mBubbleController.updateBubble(mBubbleEntry);
+ assertNotNull(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey()));
assertTrue(mBubbleController.hasBubbles());
verify(mNotificationEntryManager).updateNotifications(any());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
@@ -331,14 +352,14 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testPromoteBubble_autoExpand() throws Exception {
- mBubbleController.updateBubble(mRow2.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry2);
+ mBubbleController.updateBubble(mBubbleEntry);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
.thenReturn(mRow2.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey());
assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
@@ -361,18 +382,18 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testCancelOverflowBubble() {
- mBubbleController.updateBubble(mRow2.getEntry());
- mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */
+ mBubbleController.updateBubble(mBubbleEntry2);
+ mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */
false, /* showInShade */ true);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
.thenReturn(mRow2.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), any(), anyInt());
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
@@ -381,11 +402,11 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testUserChange_doesNotRemoveNotif() {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_CHANGED);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_CHANGED);
verify(mNotificationEntryManager, never()).performRemoveNotification(
eq(mRow.getEntry().getSbn()), any(), anyInt());
assertFalse(mBubbleController.hasBubbles());
@@ -395,15 +416,15 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testDismissStack() {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
verify(mNotificationEntryManager, times(1)).updateNotifications(any());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
- mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry2);
verify(mNotificationEntryManager, times(2)).updateNotifications(any());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
- mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mNotificationEntryManager, times(3)).updateNotifications(any());
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
@@ -417,12 +438,12 @@ public class BubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
@@ -434,7 +455,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Make sure the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Collapse
mBubbleController.collapseStack();
@@ -450,15 +471,15 @@ public class BubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mEntryListener.onPendingEntryAdded(mRow2.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow2.getEntry()));
+ mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -472,7 +493,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Last added is the one that is expanded
assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow2.getEntry()));
+ mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Switch which bubble is expanded
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
@@ -481,7 +502,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// collapse for previous bubble
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
@@ -501,12 +522,12 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testExpansionRemovesShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -520,7 +541,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -529,12 +550,12 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -548,7 +569,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -558,7 +579,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Nothing should have changed
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -568,8 +589,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mEntryListener.onPendingEntryAdded(mRow2.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -584,13 +605,13 @@ public class BubbleControllerTest extends SysuiTestCase {
assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow2.getEntry()));
+ mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Dismiss currently expanded
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey(),
- BubbleController.DISMISS_USER_GESTURE);
+ Bubbles.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
@@ -602,7 +623,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey(),
- BubbleController.DISMISS_USER_GESTURE);
+ Bubbles.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
@@ -619,7 +640,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Add the auto expand bubble
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Expansion shouldn't change
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
@@ -636,7 +657,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Add the auto expand bubble
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Expansion should change
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
@@ -653,11 +674,11 @@ public class BubbleControllerTest extends SysuiTestCase {
// Add the suppress notif bubble
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed because we were foreground
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -667,22 +688,22 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testSuppressNotif_onUpdateNotif() {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Should not be suppressed
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should show dot
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Update to suppress notif
setMetadataFlags(mRow.getEntry(),
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -695,13 +716,13 @@ public class BubbleControllerTest extends SysuiTestCase {
final String key = mRow.getEntry().getKey();
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Simulate notification cancellation.
mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
- mBubbleController.expandStackAndSelectBubble(mRow.getEntry());
+ mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
assertTrue(mSysUiStateBubblesExpanded);
}
@@ -710,7 +731,7 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -726,31 +747,31 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@Test
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@Test
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.updateBubble(mRow2.getEntry());
- mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
@Test
public void testRemoveBubble_noLongerBubbleAfterUpdate()
throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
@@ -766,7 +787,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_succeeds_appCancel() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -780,7 +801,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_entryListenerRemove() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -792,11 +813,11 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void removeBubble_clearAllIntercepted() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL_ALL);
@@ -805,17 +826,17 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(intercepted);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
}
@Test
public void removeBubble_userDismissNotifIntercepted() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
@@ -824,21 +845,21 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(intercepted);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
}
@Test
public void removeNotif_inOverflow_intercepted() {
// Get bubble with notif in shade.
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dismiss the bubble into overflow.
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
@@ -852,14 +873,14 @@ public class BubbleControllerTest extends SysuiTestCase {
public void removeNotif_notInOverflow_notIntercepted() {
// Get bubble with notif in shade.
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_NO_LONGER_BUBBLE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE);
assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
@@ -872,11 +893,11 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() {
mBubbleController.updateBubble(
- mRow.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
+ mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ false);
mBubbleController.updateBubble(
- mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
+ mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false);
mBubbleController.updateBubble(
- mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
+ mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
@@ -887,12 +908,12 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleData.setMaxOverflowBubbles(1);
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertEquals(mBubbleData.getBubbles().size(), 2);
assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
mBubbleController.removeBubble(
- mRow2.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow2.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
// Overflow max of 1 is reached; mRow is oldest, so it gets removed
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), any(), eq(REASON_CANCEL));
@@ -902,22 +923,22 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_notificationDismiss() {
- BubbleController.NotificationSuppressionChangedListener listener =
- mock(BubbleController.NotificationSuppressionChangedListener.class);
+ Bubbles.NotificationSuppressionChangedListener listener =
+ mock(Bubbles.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -926,21 +947,21 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_bubbleExpanded() {
- BubbleController.NotificationSuppressionChangedListener listener =
- mock(BubbleController.NotificationSuppressionChangedListener.class);
+ Bubbles.NotificationSuppressionChangedListener listener =
+ mock(Bubbles.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mBubbleData.setExpanded(true);
// Once a bubble is expanded the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -959,11 +980,11 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// WHEN the summary is dismissed
- mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+ mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// THEN the summary and bubbled child are suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry()));
+ groupedBubble.getEntry().getKey(), groupSummary.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
}
@@ -979,7 +1000,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// GIVEN the summary is dismissed
- mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+ mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// WHEN the summary is cancelled by the app
mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL);
@@ -1002,7 +1023,7 @@ public class BubbleControllerTest extends SysuiTestCase {
groupSummary.addChildNotification(groupedBubble);
// WHEN the summary is dismissed
- mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+ mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// THEN only the NON-bubble children are dismissed
List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
@@ -1017,7 +1038,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// THEN the bubble child is suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry()));
+ groupedBubble.getEntry().getKey(),
+ groupedBubble.getEntry().getSbn().getGroupKey()));
// THEN the summary is removed from GroupManager
verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
@@ -1031,18 +1053,18 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() {
// Setup
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
assertTrue(mBubbleController.hasBubbles());
// Overflow it
mBubbleData.dismissBubbleWithKey(mRow.getEntry().getKey(),
- BubbleController.DISMISS_USER_GESTURE);
+ Bubbles.DISMISS_USER_GESTURE);
assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse();
assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())).isTrue();
// Test
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index b9394ff3f26a..99c8ca417778 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.systemui.wmshell;
import static android.app.Notification.FLAG_BUBBLE;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
@@ -46,7 +46,6 @@ import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.face.FaceManager;
import android.os.Handler;
import android.os.PowerManager;
import android.service.dreams.IDreamManager;
@@ -60,8 +59,14 @@ import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleData;
+import com.android.systemui.bubbles.BubbleDataRepository;
+import com.android.systemui.bubbles.BubbleEntry;
+import com.android.systemui.bubbles.BubbleLogger;
+import com.android.systemui.bubbles.BubblePositioner;
+import com.android.systemui.bubbles.BubbleStackView;
+import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -69,9 +74,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
@@ -81,10 +84,8 @@ import com.android.systemui.statusbar.notification.collection.legacy.Notificatio
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -92,7 +93,6 @@ import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.util.InjectionInflationController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -111,18 +111,18 @@ import java.util.List;
/**
* Tests the NotifPipeline setup with BubbleController.
* The NotificationEntryManager setup with BubbleController is tested in
- * {@link BubbleControllerTest}.
+ * {@link BubblesTest}.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
+public class NewNotifPipelineBubblesTest extends SysuiTestCase {
@Mock
private NotificationEntryManager mNotificationEntryManager;
@Mock
private NotificationGroupManagerLegacy mNotificationGroupManager;
@Mock
- private BubbleController.NotifCallback mNotifCallback;
+ private BubblesManager.NotifCallback mNotifCallback;
@Mock
private WindowManager mWindowManager;
@Mock
@@ -136,8 +136,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Mock
private ZenModeConfig mZenModeConfig;
@Mock
- private FaceManager mFaceManager;
- @Mock
private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@@ -156,6 +154,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
+ private BubblesManager mBubblesManager;
private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private NotifCollectionListener mEntryListener;
@@ -163,8 +162,10 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
private ExpandableNotificationRow mRow;
private ExpandableNotificationRow mRow2;
private ExpandableNotificationRow mNonBubbleNotifRow;
+ private BubbleEntry mBubbleEntry;
+ private BubbleEntry mBubbleEntry2;
@Mock
- private BubbleController.BubbleExpandListener mBubbleExpandListener;
+ private Bubbles.BubbleExpandListener mBubbleExpandListener;
@Mock
private PendingIntent mDeleteIntent;
@Mock
@@ -174,16 +175,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Mock
private ShadeController mShadeController;
@Mock
- private NotificationShelfComponent mNotificationShelfComponent;
- @Mock
private NotifPipeline mNotifPipeline;
@Mock
private FeatureFlags mFeatureFlagsNewPipeline;
@Mock
private DumpManager mDumpManager;
@Mock
- private LockscreenLockIconController mLockIconController;
- @Mock
private IStatusBarService mStatusBarService;
@Mock
private LauncherApps mLauncherApps;
@@ -197,7 +194,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
private BubbleData mBubbleData;
private TestableLooper mTestableLooper;
- private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@Before
public void setUp() throws Exception {
@@ -205,26 +201,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
- mContext.addMockSystemService(FaceManager.class, mFaceManager);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
- mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext,
- new InjectionInflationController(SystemUIFactory.getInstance().getSysUIComponent()
- .createViewInstanceCreatorFactory()),
- new NotificationShelfComponent.Builder() {
- @Override
- public NotificationShelfComponent.Builder notificationShelf(
- NotificationShelf view) {
- return this;
- }
-
- @Override
- public NotificationShelfComponent build() {
- return mNotificationShelfComponent;
- }
- },
- mLockIconController);
-
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
@@ -240,6 +218,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
+ mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow.getEntry());
+ mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2.getEntry());
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
@@ -265,23 +245,9 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
mBubbleController = new TestableBubbleController(
mContext,
- mNotificationShadeWindowController,
- mStatusBarStateController,
- mShadeController,
mBubbleData,
- mConfigurationController,
- interruptionStateProvider,
- mZenModeController,
- mLockscreenUserManager,
- mNotificationGroupManager,
- mNotificationEntryManager,
- mNotifPipeline,
- mFeatureFlagsNewPipeline,
- mDumpManager,
mFloatingContentCoordinator,
mDataRepository,
- mSysUiState,
- mock(INotificationManager.class),
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
@@ -290,9 +256,28 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mock(Handler.class),
mock(ShellTaskOrganizer.class),
mPositioner);
- mBubbleController.addNotifCallback(mNotifCallback);
mBubbleController.setExpandListener(mBubbleExpandListener);
+ mBubblesManager = new BubblesManager(
+ mContext,
+ mBubbleController,
+ mNotificationShadeWindowController,
+ mStatusBarStateController,
+ mShadeController,
+ mConfigurationController,
+ mStatusBarService,
+ mock(INotificationManager.class),
+ interruptionStateProvider,
+ mZenModeController,
+ mLockscreenUserManager,
+ mNotificationGroupManager,
+ mNotificationEntryManager,
+ mNotifPipeline,
+ mSysUiState,
+ mFeatureFlagsNewPipeline,
+ mDumpManager);
+ mBubblesManager.addNotifCallback(mNotifCallback);
+
// Get a reference to the BubbleController's entry listener
verify(mNotifPipeline, atLeastOnce())
.addCollectionListener(mNotifListenerCaptor.capture());
@@ -301,26 +286,26 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testAddBubble() {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
}
@Test
public void testHasBubbles() {
assertFalse(mBubbleController.hasBubbles());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
}
@Test
public void testRemoveBubble() {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
}
@@ -328,17 +313,18 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_withDismissedNotif_inOverflow() {
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Make it look like dismissed notif
mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// We don't remove the notification since the bubble is still in overflow.
@@ -349,19 +335,20 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_withDismissedNotif_notInOverflow() {
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
.thenReturn(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Make it look like dismissed notif
mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// Since the notif is dismissed and not in overflow, once the bubble is removed,
@@ -373,15 +360,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testDismissStack() {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
- mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry2);
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
- mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey()));
@@ -393,12 +380,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
@@ -407,7 +394,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Make sure the notif is suppressed
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Collapse
mBubbleController.collapseStack();
@@ -421,15 +409,15 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
mEntryListener.onEntryAdded(mRow2.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow2.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -440,15 +428,17 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Last added is the one that is expanded
assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Switch which bubble is expanded
- mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
+ mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// collapse for previous bubble
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
@@ -467,11 +457,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testExpansionRemovesShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -483,7 +474,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -492,12 +483,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -509,7 +500,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -519,7 +510,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Nothing should have changed
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -529,8 +520,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow.getEntry());
mEntryListener.onEntryAdded(mRow2.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.updateBubble(mRow2.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -543,13 +534,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow2.getEntry()));
+ mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
// Dismiss currently expanded
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey(),
- BubbleController.DISMISS_USER_GESTURE);
+ Bubbles.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
@@ -561,7 +552,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey(),
- BubbleController.DISMISS_USER_GESTURE);
+ Bubbles.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
@@ -576,7 +567,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Add the auto expand bubble
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Expansion shouldn't change
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
@@ -591,7 +582,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Add the auto expand bubble
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Expansion should change
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
@@ -606,11 +597,11 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Add the suppress notif bubble
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed because we were foreground
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -618,22 +609,22 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testSuppressNotif_onUpdateNotif() {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Should not be suppressed
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should show dot
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
// Update to suppress notif
setMetadataFlags(mRow.getEntry(),
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -643,7 +634,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onEntryAdded(mRow.getEntry());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).showDot());
@@ -659,31 +650,31 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@Test
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@Test
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.updateBubble(mRow2.getEntry());
- mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
@Test
public void testRemoveBubble_noLongerBubbleAfterUpdate()
throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
@@ -699,7 +690,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble_entryListenerRemove() {
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -711,36 +702,36 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void removeBubble_intercepted() {
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
- boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
+ boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry());
// Intercept!
assertTrue(intercepted);
// Should update show in shade state
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
}
@Test
public void removeBubble_dismissIntoOverflow_intercepted() {
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dismiss the bubble
- mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
- boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
+ boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry());
// Intercept dismissal since bubble is going into overflow
assertTrue(intercepted);
@@ -749,19 +740,18 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void removeBubble_notIntercepted() {
mEntryListener.onEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Dismiss the bubble
- mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleController.removeBubble(mRow.getEntry().getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
- boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
+ boolean intercepted = mBubblesManager.handleDismissalInterception(mRow.getEntry());
// Not a bubble anymore so we don't intercept dismissal.
assertFalse(intercepted);
@@ -769,21 +759,21 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_notificationDismiss() {
- BubbleController.NotificationSuppressionChangedListener listener =
- mock(BubbleController.NotificationSuppressionChangedListener.class);
+ Bubbles.NotificationSuppressionChangedListener listener =
+ mock(Bubbles.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
mEntryListener.onEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
- mBubbleController.handleDismissalInterception(mRow.getEntry());
+ mBubblesManager.handleDismissalInterception(mRow.getEntry());
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -792,21 +782,21 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_bubbleExpanded() {
- BubbleController.NotificationSuppressionChangedListener listener =
- mock(BubbleController.NotificationSuppressionChangedListener.class);
+ Bubbles.NotificationSuppressionChangedListener listener =
+ mock(Bubbles.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
mEntryListener.onEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
mBubbleData.setExpanded(true);
// Once a bubble is expanded the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow.getEntry()));
+ mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -825,11 +815,12 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// WHEN the summary is dismissed
- mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+ mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// THEN the summary and bubbled child are suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry()));
+ groupedBubble.getEntry().getKey(),
+ groupedBubble.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
}
@@ -845,7 +836,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
// GIVEN the summary is dismissed
- mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+ mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// WHEN the summary is cancelled by the app
mEntryListener.onEntryRemoved(groupSummary.getEntry(), 0);
@@ -868,7 +859,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
groupSummary.addChildNotification(groupedBubble);
// WHEN the summary is dismissed
- mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+ mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// THEN only the NON-bubble children are dismissed
List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
@@ -882,11 +873,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// THEN the bubble child still exists as a bubble and is suppressed from the shade
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry()));
+ groupedBubble.getEntry().getKey(),
+ groupedBubble.getEntry().getSbn().getGroupKey()));
// THEN the summary is also suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupSummary.getEntry()));
+ groupSummary.getEntry().getKey(),
+ groupSummary.getEntry().getSbn().getGroupKey()));
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
new file mode 100644
index 000000000000..2273bc48ce8b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -0,0 +1,57 @@
+/*
+ * 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.wmshell;
+
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.os.Handler;
+import android.view.WindowManager;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.BubbleData;
+import com.android.systemui.bubbles.BubbleDataRepository;
+import com.android.systemui.bubbles.BubbleLogger;
+import com.android.systemui.bubbles.BubblePositioner;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.common.FloatingContentCoordinator;
+
+/**
+ * Testable BubbleController subclass that immediately synchronizes surfaces.
+ */
+public class TestableBubbleController extends BubbleController {
+
+ // Let's assume surfaces can be synchronized immediately.
+ TestableBubbleController(Context context,
+ BubbleData data,
+ FloatingContentCoordinator floatingContentCoordinator,
+ BubbleDataRepository dataRepository,
+ IStatusBarService statusBarService,
+ WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ LauncherApps launcherApps,
+ BubbleLogger bubbleLogger,
+ Handler mainHandler,
+ ShellTaskOrganizer shellTaskOrganizer,
+ BubblePositioner positioner) {
+ super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
+ statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
+ bubbleLogger, mainHandler, shellTaskOrganizer, positioner);
+ setInflateSynchronously(true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index 17dc76b38a56..7847c57dbc32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.systemui.wmshell;
import android.content.ContentResolver;
import android.hardware.display.AmbientDisplayConfiguration;
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
deleted file mode 100644
index 5526c657b874..000000000000
--- a/packages/Tethering/Android.bp
+++ /dev/null
@@ -1,137 +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.
-//
-
-java_defaults {
- name: "TetheringAndroidLibraryDefaults",
- sdk_version: "module_current",
- srcs: [
- "src/**/*.java",
- ":framework-tethering-shared-srcs",
- ":tethering-module-utils-srcs",
- ":services-tethering-shared-srcs",
- ],
- static_libs: [
- "androidx.annotation_annotation",
- "netd_aidl_interface-unstable-java",
- "netlink-client",
- // TODO: use networkstack-client instead of just including the AIDL interface
- "networkstack-aidl-interfaces-unstable-java",
- "android.hardware.tetheroffload.config-V1.0-java",
- "android.hardware.tetheroffload.control-V1.0-java",
- "net-utils-framework-common",
- "net-utils-device-common",
- ],
- libs: [
- "framework-statsd.stubs.module_lib",
- "framework-tethering.impl",
- "framework-wifi",
- "unsupportedappusage",
- ],
- plugins: ["java_api_finder"],
- manifest: "AndroidManifestBase.xml",
-}
-
-// Build tethering static library, used to compile both variants of the tethering.
-android_library {
- name: "TetheringApiCurrentLib",
- defaults: ["TetheringAndroidLibraryDefaults"],
-}
-
-// Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
-cc_library {
- name: "libtetherutilsjni",
- sdk_version: "current",
- apex_available: [
- "//apex_available:platform", // Used by InProcessTethering
- "com.android.tethering",
- ],
- min_sdk_version: "current",
- srcs: [
- "jni/android_net_util_TetheringUtils.cpp",
- ],
- shared_libs: [
- "liblog",
- "libnativehelper_compat_libc++",
- ],
-
- // We cannot use plain "libc++" here to link libc++ dynamically because it results in:
- // java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found
- // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't
- // build because soong complains of:
- // module Tethering missing dependencies: libc++_shared
- //
- // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries
- // we depend on do not dynamically link libc++. This is currently the case, because liblog is
- // C-only and libnativehelper_compat_libc also uses stl: "c++_static".
- stl: "c++_static",
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- "-Wthread-safety",
- ],
-
- ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"],
-}
-
-// Common defaults for compiling the actual APK.
-java_defaults {
- name: "TetheringAppDefaults",
- sdk_version: "module_current",
- privileged: true,
- jni_libs: [
- "libtetherutilsjni",
- ],
- resource_dirs: [
- "res",
- ],
- libs: [
- "framework-tethering",
- "framework-wifi",
- ],
- jarjar_rules: "jarjar-rules.txt",
- optimize: {
- proguard_flags_files: ["proguard.flags"],
- },
-}
-
-// Non-updatable tethering running in the system server process for devices not using the module
-android_app {
- name: "InProcessTethering",
- defaults: ["TetheringAppDefaults"],
- static_libs: ["TetheringApiCurrentLib"],
- certificate: "platform",
- manifest: "AndroidManifest_InProcess.xml",
- // InProcessTethering is a replacement for Tethering
- overrides: ["Tethering"],
- apex_available: ["com.android.tethering"],
- min_sdk_version: "current",
-}
-
-// Updatable tethering packaged as an application
-android_app {
- name: "Tethering",
- defaults: ["TetheringAppDefaults"],
- static_libs: ["TetheringApiCurrentLib"],
- certificate: "networkstack",
- manifest: "AndroidManifest.xml",
- use_embedded_native_libs: true,
- // The permission configuration *must* be included to ensure security of the device
- required: ["NetworkPermissionConfig"],
- apex_available: ["com.android.tethering"],
- min_sdk_version: "current",
-}
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
deleted file mode 100644
index e6444f3ead5c..000000000000
--- a/packages/Tethering/AndroidManifest.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack.tethering"
- android:sharedUserId="android.uid.networkstack">
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
-
- <!-- Permissions must be defined here, and not in the base manifest, as the tethering
- running in the system server process does not need any permission, and having
- privileged permissions added would cause crashes on startup unless they are also
- added to the privileged permissions allowlist for that package. -->
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
- <uses-permission android:name="android.permission.BROADCAST_STICKY" />
- <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
- <uses-permission android:name="android.permission.MANAGE_USB" />
- <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
- <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
- <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
- <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
- <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
-
- <protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
-
- <application
- android:process="com.android.networkstack.process"
- android:extractNativeLibs="false"
- android:persistent="true">
- <service android:name="com.android.networkstack.tethering.TetheringService"
- android:permission="android.permission.MAINLINE_NETWORK_STACK"
- android:exported="true">
- <intent-filter>
- <action android:name="android.net.ITetheringConnector"/>
- </intent-filter>
- </service>
- </application>
-</manifest>
diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml
deleted file mode 100644
index 97c3988829fe..000000000000
--- a/packages/Tethering/AndroidManifestBase.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack.tethering"
- android:versionCode="1"
- android:versionName="R-initial">
- <application
- android:label="Tethering"
- android:defaultToDeviceProtectedStorage="true"
- android:directBootAware="true"
- android:usesCleartextTraffic="true">
- </application>
-</manifest>
diff --git a/packages/Tethering/AndroidManifest_InProcess.xml b/packages/Tethering/AndroidManifest_InProcess.xml
deleted file mode 100644
index b1f124097c79..000000000000
--- a/packages/Tethering/AndroidManifest_InProcess.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack.tethering.inprocess"
- android:sharedUserId="android.uid.system"
- android:process="system">
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
- <application>
- <service android:name="com.android.networkstack.tethering.TetheringService"
- android:process="system"
- android:permission="android.permission.MAINLINE_NETWORK_STACK"
- android:exported="true">
- <intent-filter>
- <action android:name="android.net.ITetheringConnector.InProcess"/>
- </intent-filter>
- </service>
- </application>
-</manifest>
diff --git a/packages/Tethering/OWNERS b/packages/Tethering/OWNERS
deleted file mode 100644
index 5b42d490411e..000000000000
--- a/packages/Tethering/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-include platform/packages/modules/NetworkStack/:/OWNERS
-markchien@google.com
diff --git a/packages/Tethering/TEST_MAPPING b/packages/Tethering/TEST_MAPPING
deleted file mode 100644
index 5617b0c13c1c..000000000000
--- a/packages/Tethering/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "TetheringTests"
- }
- ],
- "postsubmit": [
- {
- "name": "TetheringIntegrationTests"
- }
- ]
-}
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
deleted file mode 100644
index 05243749f765..000000000000
--- a/packages/Tethering/apex/Android.bp
+++ /dev/null
@@ -1,48 +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.
-//
-
-apex {
- name: "com.android.tethering",
- updatable: true,
- min_sdk_version: "current",
- java_libs: ["framework-tethering"],
- bpfs: ["offload.o"],
- apps: ["Tethering"],
- manifest: "manifest.json",
- key: "com.android.tethering.key",
-
- androidManifest: "AndroidManifest.xml",
-}
-
-apex_key {
- name: "com.android.tethering.key",
- public_key: "com.android.tethering.avbpubkey",
- private_key: "com.android.tethering.pem",
-}
-
-android_app_certificate {
- name: "com.android.tethering.certificate",
- certificate: "com.android.tethering",
-}
-
-override_apex {
- name: "com.android.tethering.inprocess",
- base: "com.android.tethering",
- package_name: "com.android.tethering.inprocess",
- apps: [
- "InProcessTethering",
- ],
-}
diff --git a/packages/Tethering/apex/AndroidManifest.xml b/packages/Tethering/apex/AndroidManifest.xml
deleted file mode 100644
index 4aae3cc3000d..000000000000
--- a/packages/Tethering/apex/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tethering">
- <!-- APEX does not have classes.dex -->
- <application android:hasCode="false" />
- <!-- b/145383354: Current minSdk is locked to Q for development cycle, lock it to next version
- before ship. -->
- <!-- TODO: Uncomment this when the R API level is fixed. b/148281152 -->
- <!--uses-sdk
- android:minSdkVersion="29"
- android:targetSdkVersion="29"
- />
- -->
-</manifest>
diff --git a/packages/Tethering/apex/com.android.tethering.avbpubkey b/packages/Tethering/apex/com.android.tethering.avbpubkey
deleted file mode 100644
index 9a2c0174e496..000000000000
--- a/packages/Tethering/apex/com.android.tethering.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/apex/com.android.tethering.pem b/packages/Tethering/apex/com.android.tethering.pem
deleted file mode 100644
index d4f39abd3bb2..000000000000
--- a/packages/Tethering/apex/com.android.tethering.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKgIBAAKCAgEA+AWTp03PBRMGt4mVNLt5PDoFFSfmFOVTM7jt5AJXnQMIDsAM
-1cyWGWRridGIpoHAaCALVgW5aRySgi8yV5xP4w0YHcKbfh9M6I9oz4RUo4GQBZfX
-+lFIGaLjb6I3tEJxPuxps4sW26Io63ihwTnKeGyADHdHGWDUs9WU0Ml+QTvKrdjy
-qC03M0dehYXILGiA9m+UXwKoKxhWgfDUhWLhDBUtLJLPL4WeqKc9sG9h+zzVqE+8
-LzJsfrodKhTTrLpWOXi6YLRTk8dzsuPz/Nu98sJd1w3fHd20DrmkqsxVhgN1h+nk
-zcPpxyGYIP6qYVZCmIXCwZZNtPeb7y/tOs967VHoZ4Qj7p2tE0CAWFMZFGjA/pcZ
-7fi6CsIuMOYBbj4+wRlJwpG1g5zSJBCjzhv7dZp8S5oXmLShNYOMYEdsPfaZbm08
-3pVY+k8DVf7idcANXNw1lM+sPbE2hp5VuEuVpK+ca5x8hIMpTqJ84wDAjnC1kCwm
-X2xfNvYPKNF58SvqlNCPN8X7hQjoeaEb7w24vCdZMRqeGBmu1GNQvCyzbBO0huQm
-f5CQPrZjPcnoImlP879VPxY4YB6tAjsA/ZLiub9VdT108lCjb5r8criMzpMAA/AQ
-NqQLWFI3M43xPemGBTiIguTYgpRgGcdRZf7XuTgTY5qzQZZuZMVuwaqSD2cCAwEA
-AQKCAgEA0jMvw3BPTrakT7Lb8JgelKt7mUV6WyVMUZ6eh0pw5JIoJxAfEKfWYmjY
-NzKNRMjcv6LA2MP7MplTld/YI6ZHkl+Lm9VOISL39HVuV8mIThbFb+gT1INEvu1t
-IjRyT2SsQ67rmo377mLNmVtgg7mt3kfecjI44MpPGqad/CF4zmKVUKd4aI4BpYUM
-F8+dKf3bpoBEWA2RZwy2bGQmSXHW132vDoLR8y2knL04rCqJ+PrC/WWuULXEe9bS
-VtLV3yMBZq3qD4Fk/+7fILLPGvNFVdPi4htQiChYrM4rP9HzfaO63VieYMF0hR70
-pqoOznXj9Q4QVC9FZmUgFCQjQ1+KhqJw3OldIo0SnvpsLdTO/inKkhQWKC5HlPyh
-/rqvro2j3pTHWPAziuBr+oQPcdVCOlCBZ+B99L1tO7aGktVPEIVQG7G7jlFMBiJ1
-j/kRGk2RTX8RaPQJTnwUqp8mWUV2fwxHiXNadjejA5ZU3eQT2eAOhXl1w6Lv2jEl
-0wMOwPMJGcF77CcqnnWHON8fkxCbAfyy5Uo6Pm9g/Zzecn+ji2sabG7Ge5t0gzdL
-LKRcGoyakN2CrbQ8pxlCTgE4HX5oPY+VuqOf8L3AIWIJBsyLbXHVkL1mqQ/Ed2uz
-zaaSFYUZw81+m/5bl8JLPaIFNPyikZrXTD0YRer3V06XiyP/kYECggEBAP033xeF
-OhgRwkRTjd68hwRJpyHsZDWxHiUqQf6l6yFv5mEE355G2IGI7cZmR2+tUDjQdxLv
-tAZIszTK4PFCdVTeWfGVFbVF84eNWLB124pHDMM79GN/AMcuHnQPR756a8IO1hIy
-4KxIUE1a1PKN5b9IgE5Lu4TZM96HDpFcUAmCT5urdYDmg3++IWT9PYQlGS7Hhiar
-r+Hh646waM8Qx619CwXBqy+Y37+WHVbYqJClr6AcpVMrGA+6cgpskFpZAPLsoy7G
-RSsVfyV8pH2JKm/hzk7XCwIpczxeWQSfpJWZ+oOPFHu+zM60Cdj2UrQyKrNHwew8
-+WYe9eCA+MiNBcECggEBAPq/F1vdqROiLv9uzhKb8ybgdL7CmREELiqwK+MvNE9t
-W7lQz7lcWzav+b2n0M+VJBxUWB3XClgoIvA/AllgTgsYXfKAxNakhKLSBoMmvKCW
-HtWcGr/D3RcmacK+DTMWlVS/LuueAFLuH6UmBIUFKc+qA5x7oQecAFALBFupE3G4
-LtAspLBI6P8gRtRav5p2whs9H8qjYcyf2f6liWpkmFITcXvPvAxFHicR6ZJdwZ/S
-PiX2LJQnOpT7L3+2PWnYwzFStb4MkMGlFKcscU9CvS53JcP/J4Asjk0I4zDB2gri
-xzFHPlVzCr2IVVGptKCQ3sdYiMIzQKzEXQHCU8h37ycCggEBAJu8aC48Fz3Edlm1
-ldS+2L9vWSaJEBzhoSu0cMBgZVu8SdGzwKDE69XHVI4oS5lI28UFmaaA3JTc07MN
-cAmSGT2oP2NQkPhbXGsrKLfm1K6YAiZ1Ulp7OwxFth8lYreo7Wt92nV46yuqkhDx
-Y3UGhp39xkPhWiRbvgYHxJLsVqFyjumsK2mq3IeNdVZ6VgJXGsTlnAFeqJ7hZxHs
-N5natSRjeosA0PtGJ57agZLvT8Ue0gREef3LzFGoFwmIOcQHZ4kAt2BGOzZDU17H
-6Rb4bKxBEbT1l2St/5zKXi90zDHicOvG7Q8qiyY6HrBc1wLSs+ZtpLxZx/3h3tFE
-IT6fVUECggEBAMSAQm8Ey76OJ+SXUjk1K50442SnHcs/Cmr7urkEQitImUwl71Pk
-87pst/uP6szypOTqmE9yOTIS6iZ6Sn3+QcriIqWrkhZfwW3Tx7S6A7KZUrq15iSH
-+thsiw9JXxC9TvOmC8AsBzb2U6hZncsc28JZCxFztSNAduJDb/vhCVLiMxWDFuDr
-kmR1R+yc3XDQRpeQFDz6QudYEj9EPOc6xD/16sZLaqP2+oVFvVSt0tJLsdaQECle
-gMNGAdhE2eX8MCOUHMc+E6cdlozYAEhMFfO2/cqWR79jq3TlVR3dnOFRDScqHMhc
-KnuTvsELjHkUbvGsCSiff7yk+fop7vy4OJsCggEAPemJdItO2rhib8EofrZdY72I
-oifX1jhPZ1BWD2GKgcx+eVyJGbONBbJVexvvskTfZBvCcAegmgp+sngP6MO6yZkr
-cHMfAJeApYZnshsgXksHGMDtSB50/w1JLrc/nqpxdpy/aTazt0Eu1pLWpze1HFZ/
-Xyu4PcmrU+4P1vN7c396slHMktEvly6QqOn4nfBbGDJ17Ow6X1XFvGjAxQPIDTB+
-6loV14AHymwmqwMrGn84O72rzqyw+41GxW5+oXhOZ4MeXF3u89TBLWvXDpPy/YQU
-EiKpodN0YeEn6Ghzplan8rUha+7TP7AYnS5pCszsCHKd03Py0lMLkF+uAfVsDA==
------END RSA PRIVATE KEY-----
diff --git a/packages/Tethering/apex/com.android.tethering.pk8 b/packages/Tethering/apex/com.android.tethering.pk8
deleted file mode 100644
index 3b94405945cb..000000000000
--- a/packages/Tethering/apex/com.android.tethering.pk8
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/apex/com.android.tethering.x509.pem b/packages/Tethering/apex/com.android.tethering.x509.pem
deleted file mode 100644
index a1786e35e854..000000000000
--- a/packages/Tethering/apex/com.android.tethering.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGKTCCBBGgAwIBAgIUNiSs5EMqxCZ31gWWCcRJVp9HffAwDQYJKoZIhvcNAQEL
-BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
-DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
-b2lkMR4wHAYDVQQDDBVjb20uYW5kcm9pZC50ZXRoZXJpbmcxIjAgBgkqhkiG9w0B
-CQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjE4MDcwMDQ4WhgPNDc1NzEx
-MTMwNzAwNDhaMIGiMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW
-MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE
-CwwHQW5kcm9pZDEeMBwGA1UEAwwVY29tLmFuZHJvaWQudGV0aGVyaW5nMSIwIAYJ
-KoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEF
-AAOCAg8AMIICCgKCAgEAxvTUA4seblYjZLfTVNwZuJH914QVNFTj+vD94pWmt5Aq
-sH1DVTpBvpXXegc/P5HI2XF/71poSBib1WaQSuXG0fU5K75T18bOGL0qF+fhMtBO
-wUyvulcjO0h4XE/xf0txY54exUjAA4JS9ERGJOgb4GOwSbPyzekfmzIyCZ2Yawwu
-+oGwD2ZNzZRaPOoWxjwohBWQ6mySuvF9RRRb300qmxxUGFM9Ki3aqrWlYlHEOwOC
-M+gIXxYFO7S+yUzf6/gMZLOz2YqfcTOup4hAxtExR7niutxJSsRLPBL237exAJoz
-OupoXjtWAlPK4ZwZ/Nl1jdTWauJ+Kv3WqzhHGEb2gn3ZpeO3IdOjJhDgFJ6m1OT/
-kjRbW1LCuKGrKaoqsEDT2X3a7Izfripn65hSNTfR5gNLtgELaI3/vXi8Fmzw1AfH
-+qi6ulElZvSwx0qm+S0QiPyGFlxrsdnHoGJl1tzjJW8KdNZRvzRLUQtbphPp+VkL
-5i0bNKum+AwbfdUkLkNLfw9XdbujgBkZTZDQbZGsNjgrvyXcPO2KiJee0hVCZRs0
-rhDi5Pfm7BnN/I2vaTRz/W4mdct9H2RWMuqlSH90JvmKtWcND8ahmOJ3sggrvzfO
-QNs3k4JTRecamMzqIkylhlnEC4FjWc6Bx4wsEpwBMZOkF/tGGMZYf2C09a8tpP0C
-AwEAAaNTMFEwHQYDVR0OBBYEFNP5gIpNWmq0xa411M1GaRPbEijvMB8GA1UdIwQY
-MBaAFNP5gIpNWmq0xa411M1GaRPbEijvMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
-hvcNAQELBQADggIBADJGmU3QP4EGbt6eBhVPeo/efsqrHsuB2fvFzvIobJbfkSob
-cmvjbzIikOlPAgFWj8lT5SDcIWRorFf1u2JylClJ0nSDcqJMHVKmT7wseV/KtX//
-1yUyJFRQVzmjC89dp8OIc00GmItivKLer3NbJdkR3rTUjg7+bNUO27Qp3AFREmiJ
-P+M7ouvcQRvByUWbp/LOrJpMdJLysRBO562RwrtwTjltdvufyYswbBZOKEiUh1Jc
-Ged+3+SJdhwq3Wy+R3Uj7YE7mUMu1QNbANIMrwF8W93EA53eoL2+cKmuaVU6ZURL
-xgSJaY6TrunnSI9XTROLtjsFlJorYWy2tvG7Q5Hw3OkO2Xdz/mm85VTkiusg9DMB
-WWTv607YtsIO0FhKmcV4bp3q/EkRj3t/zLvL9uFJrWDGkuShZq6fQvqbCvaokOPY
-+M0ZRIwgwa9UpEE0BMklVWqR6BGyap614gOgcOjYM70WRNl59Qne+g128ZN7g9nz
-61F70i7kUngV0ZUz1/Fu/NCG+6wGF85ZbFmQl60YHPDw1FtjVUuKyBblaDzdJunx
-yQr2t9RUokzFBFK0lGW3+yf0WDQ5fqTMs5h8bz1FCq8/HzWmpdOfqePLe4zsld3b
-1nFuSohaIfbn/HDdTNtTBGQPgz8ZswQ6ejJJqTLz9D/odbqn9LeIhDZXcQTf
------END CERTIFICATE-----
diff --git a/packages/Tethering/apex/manifest.json b/packages/Tethering/apex/manifest.json
deleted file mode 100644
index 11e205d1b7ab..000000000000
--- a/packages/Tethering/apex/manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.tethering",
- "version": 309999900
-}
diff --git a/packages/Tethering/bpf_progs/Android.bp b/packages/Tethering/bpf_progs/Android.bp
deleted file mode 100644
index d54f86148665..000000000000
--- a/packages/Tethering/bpf_progs/Android.bp
+++ /dev/null
@@ -1,33 +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.
-//
-
-//
-// bpf kernel programs
-//
-bpf {
- name: "offload.o",
- srcs: ["offload.c"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- include_dirs: [
- // TODO: get rid of system/netd.
- "system/netd/bpf_progs", // for bpf_net_helpers.h
- "system/netd/libnetdbpf/include", // for bpf_shared.h
- "system/netd/libnetdutils/include", // for UidConstants.h
- ],
-}
diff --git a/packages/Tethering/bpf_progs/offload.c b/packages/Tethering/bpf_progs/offload.c
deleted file mode 100644
index cc5af3127b02..000000000000
--- a/packages/Tethering/bpf_progs/offload.c
+++ /dev/null
@@ -1,207 +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.
- */
-
-#include <linux/if.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <linux/pkt_cls.h>
-#include <linux/tcp.h>
-
-#include "bpf_helpers.h"
-#include "bpf_net_helpers.h"
-#include "netdbpf/bpf_shared.h"
-
-DEFINE_BPF_MAP_GRW(tether_ingress_map, HASH, TetherIngressKey, TetherIngressValue, 64,
- AID_NETWORK_STACK)
-
-// Tethering stats, indexed by upstream interface.
-DEFINE_BPF_MAP_GRW(tether_stats_map, HASH, uint32_t, TetherStatsValue, 16, AID_NETWORK_STACK)
-
-// Tethering data limit, indexed by upstream interface.
-// (tethering allowed when stats[iif].rxBytes + stats[iif].txBytes < limit[iif])
-DEFINE_BPF_MAP_GRW(tether_limit_map, HASH, uint32_t, uint64_t, 16, AID_NETWORK_STACK)
-
-static inline __always_inline int do_forward(struct __sk_buff* skb, bool is_ethernet) {
- int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
- void* data = (void*)(long)skb->data;
- const void* data_end = (void*)(long)skb->data_end;
- struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet
- struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data;
-
- // Must be meta-ethernet IPv6 frame
- if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK;
-
- // Must have (ethernet and) ipv6 header
- if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK;
-
- // Ethertype - if present - must be IPv6
- if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK;
-
- // IP version must be 6
- if (ip6->version != 6) return TC_ACT_OK;
-
- // Cannot decrement during forward if already zero or would be zero,
- // Let the kernel's stack handle these cases and generate appropriate ICMP errors.
- if (ip6->hop_limit <= 1) return TC_ACT_OK;
-
- // Protect against forwarding packets sourced from ::1 or fe80::/64 or other weirdness.
- __be32 src32 = ip6->saddr.s6_addr32[0];
- if (src32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP
- (src32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast
- return TC_ACT_OK;
-
- TetherIngressKey k = {
- .iif = skb->ifindex,
- .neigh6 = ip6->daddr,
- };
-
- TetherIngressValue* v = bpf_tether_ingress_map_lookup_elem(&k);
-
- // If we don't find any offload information then simply let the core stack handle it...
- if (!v) return TC_ACT_OK;
-
- uint32_t stat_and_limit_k = skb->ifindex;
-
- TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k);
-
- // If we don't have anywhere to put stats, then abort...
- if (!stat_v) return TC_ACT_OK;
-
- uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k);
-
- // If we don't have a limit, then abort...
- if (!limit_v) return TC_ACT_OK;
-
- // Required IPv6 minimum mtu is 1280, below that not clear what we should do, abort...
- const int pmtu = v->pmtu;
- if (pmtu < IPV6_MIN_MTU) return TC_ACT_OK;
-
- // Approximate handling of TCP/IPv6 overhead for incoming LRO/GRO packets: default
- // outbound path mtu of 1500 is not necessarily correct, but worst case we simply
- // undercount, which is still better then not accounting for this overhead at all.
- // Note: this really shouldn't be device/path mtu at all, but rather should be
- // derived from this particular connection's mss (ie. from gro segment size).
- // This would require a much newer kernel with newer ebpf accessors.
- // (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header)
- uint64_t packets = 1;
- uint64_t bytes = skb->len;
- if (bytes > pmtu) {
- const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12;
- const int mss = pmtu - tcp_overhead;
- const uint64_t payload = bytes - tcp_overhead;
- packets = (payload + mss - 1) / mss;
- bytes = tcp_overhead * packets + payload;
- }
-
- // Are we past the limit? If so, then abort...
- // Note: will not overflow since u64 is 936 years even at 5Gbps.
- // Do not drop here. Offload is just that, whenever we fail to handle
- // a packet we let the core stack deal with things.
- // (The core stack needs to handle limits correctly anyway,
- // since we don't offload all traffic in both directions)
- if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) return TC_ACT_OK;
-
- if (!is_ethernet) {
- is_ethernet = true;
- l2_header_size = sizeof(struct ethhdr);
- // Try to inject an ethernet header, and simply return if we fail
- if (bpf_skb_change_head(skb, l2_header_size, /*flags*/ 0)) {
- __sync_fetch_and_add(&stat_v->rxErrors, 1);
- return TC_ACT_OK;
- }
-
- // bpf_skb_change_head() invalidates all pointers - reload them
- data = (void*)(long)skb->data;
- data_end = (void*)(long)skb->data_end;
- eth = data;
- ip6 = (void*)(eth + 1);
-
- // I do not believe this can ever happen, but keep the verifier happy...
- if (data + l2_header_size + sizeof(*ip6) > data_end) {
- __sync_fetch_and_add(&stat_v->rxErrors, 1);
- return TC_ACT_SHOT;
- }
- };
-
- // CHECKSUM_COMPLETE is a 16-bit one's complement sum,
- // thus corrections for it need to be done in 16-byte chunks at even offsets.
- // IPv6 nexthdr is at offset 6, while hop limit is at offset 7
- uint8_t old_hl = ip6->hop_limit;
- --ip6->hop_limit;
- uint8_t new_hl = ip6->hop_limit;
-
- // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
- // (-ENOTSUPP) if it isn't.
- bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl));
-
- __sync_fetch_and_add(&stat_v->rxPackets, packets);
- __sync_fetch_and_add(&stat_v->rxBytes, bytes);
-
- // Overwrite any mac header with the new one
- *eth = v->macHeader;
-
- // Redirect to forwarded interface.
- //
- // Note that bpf_redirect() cannot fail unless you pass invalid flags.
- // The redirect actually happens after the ebpf program has already terminated,
- // and can fail for example for mtu reasons at that point in time, but there's nothing
- // we can do about it here.
- return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
-}
-
-SEC("schedcls/ingress/tether_ether")
-int sched_cls_ingress_tether_ether(struct __sk_buff* skb) {
- return do_forward(skb, true);
-}
-
-// Note: section names must be unique to prevent programs from appending to each other,
-// so instead the bpf loader will strip everything past the final $ symbol when actually
-// pinning the program into the filesystem.
-//
-// bpf_skb_change_head() is only present on 4.14+ and 2 trivial kernel patches are needed:
-// ANDROID: net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head
-// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu
-// (the first of those has already been upstreamed)
-//
-// 5.4 kernel support was only added to Android Common Kernel in R,
-// and thus a 5.4 kernel always supports this.
-//
-// Hence, this mandatory (must load successfully) implementation for 5.4+ kernels:
-DEFINE_BPF_PROG_KVER("schedcls/ingress/tether_rawip$5_4", AID_ROOT, AID_ROOT,
- sched_cls_ingress_tether_rawip_5_4, KVER(5, 4, 0))
-(struct __sk_buff* skb) {
- return do_forward(skb, false);
-}
-
-// and this identical optional (may fail to load) implementation for [4.14..5.4) patched kernels:
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$4_14", AID_ROOT, AID_ROOT,
- sched_cls_ingress_tether_rawip_4_14, KVER(4, 14, 0),
- KVER(5, 4, 0))
-(struct __sk_buff* skb) {
- return do_forward(skb, false);
-}
-
-// and define a no-op stub for [4.9,4.14) and unpatched [4.14,5.4) kernels.
-// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned
-// it at the same location this one would be pinned at and will thus skip loading this stub)
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$stub", AID_ROOT, AID_ROOT,
- sched_cls_ingress_tether_rawip_stub, KVER_NONE, KVER(5, 4, 0))
-(struct __sk_buff* skb) {
- return TC_ACT_OK;
-}
-
-LICENSE("Apache 2.0");
-CRITICAL("netd");
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
deleted file mode 100644
index bf643cdcecc5..000000000000
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ /dev/null
@@ -1,47 +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.
-
-java_sdk_library {
- name: "framework-tethering",
- defaults: ["framework-module-defaults"],
- impl_library_visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
-
- srcs: [":framework-tethering-srcs"],
-
- jarjar_rules: "jarjar-rules.txt",
- installable: true,
-
- hostdex: true, // for hiddenapi check
- apex_available: ["com.android.tethering"],
- permitted_packages: ["android.net"],
-}
-
-filegroup {
- name: "framework-tethering-srcs",
- srcs: [
- "src/android/net/TetheredClient.aidl",
- "src/android/net/TetheredClient.java",
- "src/android/net/TetheringManager.java",
- "src/android/net/TetheringConstants.java",
- "src/android/net/IIntResultListener.aidl",
- "src/android/net/ITetheringEventCallback.aidl",
- "src/android/net/ITetheringConnector.aidl",
- "src/android/net/TetheringCallbackStartedParcel.aidl",
- "src/android/net/TetheringConfigurationParcel.aidl",
- "src/android/net/TetheringRequestParcel.aidl",
- "src/android/net/TetherStatesParcel.aidl",
- ],
- path: "src"
-}
diff --git a/packages/Tethering/common/TetheringLib/api/current.txt b/packages/Tethering/common/TetheringLib/api/current.txt
deleted file mode 100644
index d802177e249b..000000000000
--- a/packages/Tethering/common/TetheringLib/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
deleted file mode 100644
index 6ddb122936e7..000000000000
--- a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-// Signature format: 2.0
-package android.net {
-
- public final class TetheringConstants {
- field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
- field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
- field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
- field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
- field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
- }
-
- public class TetheringManager {
- ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
- method public int getLastTetherError(@NonNull String);
- method @NonNull public String[] getTetherableBluetoothRegexs();
- method @NonNull public String[] getTetherableIfaces();
- method @NonNull public String[] getTetherableUsbRegexs();
- method @NonNull public String[] getTetherableWifiRegexs();
- method @NonNull public String[] getTetheredIfaces();
- method @NonNull public String[] getTetheringErroredIfaces();
- method public boolean isTetheringSupported();
- method public boolean isTetheringSupported(@NonNull String);
- method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
- method @Deprecated public int setUsbTethering(boolean);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @Deprecated public int tether(@NonNull String);
- method @Deprecated public int untether(@NonNull String);
- }
-
- public static interface TetheringManager.TetheringEventCallback {
- method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
- }
-
- public static class TetheringManager.TetheringInterfaceRegexps {
- method @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
- method @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
- method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
- }
-
-}
-
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt b/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt
deleted file mode 100644
index d802177e249b..000000000000
--- a/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/removed.txt b/packages/Tethering/common/TetheringLib/api/removed.txt
deleted file mode 100644
index d802177e249b..000000000000
--- a/packages/Tethering/common/TetheringLib/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/system-current.txt b/packages/Tethering/common/TetheringLib/api/system-current.txt
deleted file mode 100644
index edd1ebb5f751..000000000000
--- a/packages/Tethering/common/TetheringLib/api/system-current.txt
+++ /dev/null
@@ -1,99 +0,0 @@
-// Signature format: 2.0
-package android.net {
-
- public final class TetheredClient implements android.os.Parcelable {
- ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
- method public int describeContents();
- method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
- method @NonNull public android.net.MacAddress getMacAddress();
- method public int getTetheringType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
- }
-
- public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.net.LinkAddress getAddress();
- method @Nullable public String getHostname();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
- }
-
- public class TetheringManager {
- method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
- field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
- field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
- field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
- field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
- field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
- field public static final int TETHERING_BLUETOOTH = 2; // 0x2
- field public static final int TETHERING_ETHERNET = 5; // 0x5
- field public static final int TETHERING_INVALID = -1; // 0xffffffff
- field public static final int TETHERING_NCM = 4; // 0x4
- field public static final int TETHERING_USB = 1; // 0x1
- field public static final int TETHERING_WIFI = 0; // 0x0
- field public static final int TETHERING_WIFI_P2P = 3; // 0x3
- field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
- field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
- field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
- field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
- field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
- field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
- field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
- field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
- field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
- field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
- field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
- field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
- field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
- field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
- field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
- field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
- field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
- field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
- field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
- field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
- }
-
- public static interface TetheringManager.OnTetheringEntitlementResultListener {
- method public void onTetheringEntitlementResult(int);
- }
-
- public static interface TetheringManager.StartTetheringCallback {
- method public default void onTetheringFailed(int);
- method public default void onTetheringStarted();
- }
-
- public static interface TetheringManager.TetheringEventCallback {
- method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
- method public default void onError(@NonNull String, int);
- method public default void onOffloadStatusChanged(int);
- method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheringSupported(boolean);
- method public default void onUpstreamChanged(@Nullable android.net.Network);
- }
-
- public static class TetheringManager.TetheringRequest {
- method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
- method @Nullable public android.net.LinkAddress getLocalIpv4Address();
- method public boolean getShouldShowEntitlementUi();
- method public int getTetheringType();
- method public boolean isExemptFromEntitlementCheck();
- }
-
- public static class TetheringManager.TetheringRequest.Builder {
- ctor public TetheringManager.TetheringRequest.Builder(int);
- method @NonNull public android.net.TetheringManager.TetheringRequest build();
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
- }
-
-}
-
diff --git a/packages/Tethering/common/TetheringLib/api/system-removed.txt b/packages/Tethering/common/TetheringLib/api/system-removed.txt
deleted file mode 100644
index d802177e249b..000000000000
--- a/packages/Tethering/common/TetheringLib/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/jarjar-rules.txt b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
deleted file mode 100644
index e459fad54993..000000000000
--- a/packages/Tethering/common/TetheringLib/jarjar-rules.txt
+++ /dev/null
@@ -1 +0,0 @@
-# jarjar rules for the bootclasspath tethering framework library here \ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl b/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl
deleted file mode 100644
index c3d66ee14526..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl
+++ /dev/null
@@ -1,25 +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.net;
-
-/**
- * Listener interface allowing objects to listen to various module event.
- * {@hide}
- */
-oneway interface IIntResultListener {
- void onResult(int resultCode);
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
deleted file mode 100644
index cf094aac2cbe..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net;
-
-import android.net.IIntResultListener;
-import android.net.ITetheringEventCallback;
-import android.net.TetheringRequestParcel;
-import android.os.ResultReceiver;
-
-/** @hide */
-oneway interface ITetheringConnector {
- void tether(String iface, String callerPkg, String callingAttributionTag,
- IIntResultListener receiver);
-
- void untether(String iface, String callerPkg, String callingAttributionTag,
- IIntResultListener receiver);
-
- void setUsbTethering(boolean enable, String callerPkg,
- String callingAttributionTag, IIntResultListener receiver);
-
- void startTethering(in TetheringRequestParcel request, String callerPkg,
- String callingAttributionTag, IIntResultListener receiver);
-
- void stopTethering(int type, String callerPkg, String callingAttributionTag,
- IIntResultListener receiver);
-
- void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
- boolean showEntitlementUi, String callerPkg, String callingAttributionTag);
-
- void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
-
- void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
-
- void isTetheringSupported(String callerPkg, String callingAttributionTag,
- IIntResultListener receiver);
-
- void stopAllTethering(String callerPkg, String callingAttributionTag,
- IIntResultListener receiver);
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
deleted file mode 100644
index b4e3ba46791c..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
+++ /dev/null
@@ -1,39 +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.net;
-
-import android.net.Network;
-import android.net.TetheredClient;
-import android.net.TetheringConfigurationParcel;
-import android.net.TetheringCallbackStartedParcel;
-import android.net.TetherStatesParcel;
-
-/**
- * Callback class for receiving tethering changed events.
- * @hide
- */
-oneway interface ITetheringEventCallback
-{
- /** Called immediately after the callbacks are registered */
- void onCallbackStarted(in TetheringCallbackStartedParcel parcel);
- void onCallbackStopped(int errorCode);
- void onUpstreamChanged(in Network network);
- void onConfigurationChanged(in TetheringConfigurationParcel config);
- void onTetherStatesChanged(in TetherStatesParcel states);
- void onTetherClientsChanged(in List<TetheredClient> clients);
- void onOffloadStatusChanged(int status);
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
deleted file mode 100644
index 3d842b337428..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
+++ /dev/null
@@ -1,31 +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.net;
-
-/**
- * Status details for tethering downstream interfaces.
- * {@hide}
- */
-parcelable TetherStatesParcel {
- String[] availableList;
- String[] tetheredList;
- String[] localOnlyList;
- String[] erroredIfaceList;
- // List of Last error code corresponding to each errored iface in erroredIfaceList. */
- // TODO: Improve this as b/143122247.
- int[] lastErrorList;
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
deleted file mode 100644
index 0b223f42b954..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Information on a tethered downstream client.
- * @hide
- */
-@SystemApi
-public final class TetheredClient implements Parcelable {
- @NonNull
- private final MacAddress mMacAddress;
- @NonNull
- private final List<AddressInfo> mAddresses;
- // TODO: use an @IntDef here
- private final int mTetheringType;
-
- public TetheredClient(@NonNull MacAddress macAddress,
- @NonNull Collection<AddressInfo> addresses, int tetheringType) {
- mMacAddress = macAddress;
- mAddresses = new ArrayList<>(addresses);
- mTetheringType = tetheringType;
- }
-
- private TetheredClient(@NonNull Parcel in) {
- this(in.readParcelable(null), in.createTypedArrayList(AddressInfo.CREATOR), in.readInt());
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelable(mMacAddress, flags);
- dest.writeTypedList(mAddresses);
- dest.writeInt(mTetheringType);
- }
-
- /**
- * Get the MAC address used to identify the client.
- */
- @NonNull
- public MacAddress getMacAddress() {
- return mMacAddress;
- }
-
- /**
- * Get information on the list of addresses that are associated with the client.
- */
- @NonNull
- public List<AddressInfo> getAddresses() {
- return new ArrayList<>(mAddresses);
- }
-
- /**
- * Get the type of tethering used by the client.
- * @return one of the {@code TetheringManager#TETHERING_*} constants.
- */
- public int getTetheringType() {
- return mTetheringType;
- }
-
- /**
- * Return a new {@link TetheredClient} that has all the attributes of this instance, plus the
- * {@link AddressInfo} of the provided {@link TetheredClient}.
- *
- * <p>Duplicate addresses are removed.
- * @hide
- */
- public TetheredClient addAddresses(@NonNull TetheredClient other) {
- final LinkedHashSet<AddressInfo> newAddresses = new LinkedHashSet<>(
- mAddresses.size() + other.mAddresses.size());
- newAddresses.addAll(mAddresses);
- newAddresses.addAll(other.mAddresses);
- return new TetheredClient(mMacAddress, newAddresses, mTetheringType);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mMacAddress, mAddresses, mTetheringType);
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (!(obj instanceof TetheredClient)) return false;
- final TetheredClient other = (TetheredClient) obj;
- return mMacAddress.equals(other.mMacAddress)
- && mAddresses.equals(other.mAddresses)
- && mTetheringType == other.mTetheringType;
- }
-
- /**
- * Information on an lease assigned to a tethered client.
- */
- public static final class AddressInfo implements Parcelable {
- @NonNull
- private final LinkAddress mAddress;
- @Nullable
- private final String mHostname;
-
- /** @hide */
- public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) {
- this.mAddress = address;
- this.mHostname = hostname;
- }
-
- private AddressInfo(Parcel in) {
- this(in.readParcelable(null), in.readString());
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelable(mAddress, flags);
- dest.writeString(mHostname);
- }
-
- /**
- * Get the link address (including prefix length and lifetime) used by the client.
- *
- * This may be an IPv4 or IPv6 address.
- */
- @NonNull
- public LinkAddress getAddress() {
- return mAddress;
- }
-
- /**
- * Get the hostname that was advertised by the client when obtaining its address, if any.
- */
- @Nullable
- public String getHostname() {
- return mHostname;
- }
-
- /**
- * Get the expiration time of the address assigned to the client.
- * @hide
- */
- public long getExpirationTime() {
- return mAddress.getExpirationTime();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mAddress, mHostname);
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (!(obj instanceof AddressInfo)) return false;
- final AddressInfo other = (AddressInfo) obj;
- // Use .equals() for addresses as all changes, including address expiry changes,
- // should be included.
- return other.mAddress.equals(mAddress)
- && Objects.equals(mHostname, other.mHostname);
- }
-
- @NonNull
- public static final Creator<AddressInfo> CREATOR = new Creator<AddressInfo>() {
- @NonNull
- @Override
- public AddressInfo createFromParcel(@NonNull Parcel in) {
- return new AddressInfo(in);
- }
-
- @NonNull
- @Override
- public AddressInfo[] newArray(int size) {
- return new AddressInfo[size];
- }
- };
-
- @NonNull
- @Override
- public String toString() {
- return "AddressInfo {"
- + mAddress
- + (mHostname != null ? ", hostname " + mHostname : "")
- + "}";
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- public static final Creator<TetheredClient> CREATOR = new Creator<TetheredClient>() {
- @NonNull
- @Override
- public TetheredClient createFromParcel(@NonNull Parcel in) {
- return new TetheredClient(in);
- }
-
- @NonNull
- @Override
- public TetheredClient[] newArray(int size) {
- return new TetheredClient[size];
- }
- };
-
- @NonNull
- @Override
- public String toString() {
- return "TetheredClient {hwAddr " + mMacAddress
- + ", addresses " + mAddresses
- + ", tetheringType " + mTetheringType
- + "}";
- }
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
deleted file mode 100644
index 253eacbd23e7..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.net.Network;
-import android.net.TetheredClient;
-import android.net.TetheringConfigurationParcel;
-import android.net.TetherStatesParcel;
-
-/**
- * Initial information reported by tethering upon callback registration.
- * @hide
- */
-parcelable TetheringCallbackStartedParcel {
- boolean tetheringSupported;
- Network upstreamNetwork;
- TetheringConfigurationParcel config;
- TetherStatesParcel states;
- List<TetheredClient> tetheredClients;
- int offloadStatus;
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl
deleted file mode 100644
index 89f38132ffad..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl
+++ /dev/null
@@ -1,37 +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.net;
-
-/**
- * Configuration details for tethering.
- * @hide
- */
-parcelable TetheringConfigurationParcel {
- int subId;
- String[] tetherableUsbRegexs;
- String[] tetherableWifiRegexs;
- String[] tetherableBluetoothRegexs;
- boolean isDunRequired;
- boolean chooseUpstreamAutomatically;
- int[] preferredUpstreamIfaceTypes;
- String[] legacyDhcpRanges;
- String[] defaultIPv4DNS;
- boolean enableLegacyDhcpServer;
- String[] provisioningApp;
- String provisioningAppNoUi;
- int provisioningCheckPeriod;
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
deleted file mode 100644
index f14def6a3a02..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
-import android.annotation.SystemApi;
-import android.os.ResultReceiver;
-
-/**
- * Collections of constants for internal tethering usage.
- *
- * <p>These hidden constants are not in TetheringManager as they are not part of the API stubs
- * generated for TetheringManager, which prevents the tethering module from linking them at
- * build time.
- * TODO: investigate changing the tethering build rules so that Tethering can reference hidden
- * symbols from framework-tethering even when they are in a non-hidden class.
- * @hide
- */
-@SystemApi(client = MODULE_LIBRARIES)
-public final class TetheringConstants {
- /** An explicit private class to avoid exposing constructor.*/
- private TetheringConstants() { }
-
- /**
- * Extra used for communicating with the TetherService and TetherProvisioningActivity.
- * Includes the type of tethering to enable if any.
- */
- public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
- /**
- * Extra used for communicating with the TetherService. Includes the type of tethering for
- * which to cancel provisioning.
- */
- public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
- /**
- * Extra used for communicating with the TetherService. True to schedule a recheck of tether
- * provisioning.
- */
- public static final String EXTRA_SET_ALARM = "extraSetAlarm";
- /**
- * Tells the TetherService to run a provision check now.
- */
- public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
- /**
- * Extra used for communicating with the TetherService and TetherProvisioningActivity.
- * Contains the {@link ResultReceiver} which will receive provisioning results.
- * Can not be empty.
- */
- public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
-
- /**
- * Extra used for communicating with the TetherService and TetherProvisioningActivity.
- * Contains the subId of current active cellular upstream.
- * @hide
- */
- public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID";
-
- /**
- * Extra used for telling TetherProvisioningActivity the entitlement package name and class
- * name to start UI entitlement check.
- * @hide
- */
- public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME =
- "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME";
-
- /**
- * Extra used for telling TetherService the intent action to start silent entitlement check.
- * @hide
- */
- public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION =
- "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION";
-
- /**
- * Extra used for TetherService to receive the response of provisioning check.
- * @hide
- */
- public static final String EXTRA_TETHER_PROVISIONING_RESPONSE =
- "android.net.extra.TETHER_PROVISIONING_RESPONSE";
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
deleted file mode 100644
index 13b05a841d24..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ /dev/null
@@ -1,1364 +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.net;
-
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.function.Supplier;
-
-/**
- * This class provides the APIs to control the tethering service.
- * <p> The primary responsibilities of this class are to provide the APIs for applications to
- * start tethering, stop tethering, query configuration and query status.
- *
- * @hide
- */
-@SystemApi
-public class TetheringManager {
- private static final String TAG = TetheringManager.class.getSimpleName();
- private static final int DEFAULT_TIMEOUT_MS = 60_000;
- private static final long CONNECTOR_POLL_INTERVAL_MILLIS = 200L;
-
- @GuardedBy("mConnectorWaitQueue")
- @Nullable
- private ITetheringConnector mConnector;
- @GuardedBy("mConnectorWaitQueue")
- @NonNull
- private final List<ConnectorConsumer> mConnectorWaitQueue = new ArrayList<>();
- private final Supplier<IBinder> mConnectorSupplier;
-
- private final TetheringCallbackInternal mCallback;
- private final Context mContext;
- private final ArrayMap<TetheringEventCallback, ITetheringEventCallback>
- mTetheringEventCallbacks = new ArrayMap<>();
-
- private volatile TetheringConfigurationParcel mTetheringConfiguration;
- private volatile TetherStatesParcel mTetherStatesParcel;
-
- /**
- * Broadcast Action: A tetherable connection has come or gone.
- * Uses {@code TetheringManager.EXTRA_AVAILABLE_TETHER},
- * {@code TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY},
- * {@code TetheringManager.EXTRA_ACTIVE_TETHER}, and
- * {@code TetheringManager.EXTRA_ERRORED_TETHER} to indicate
- * the current state of tethering. Each include a list of
- * interface names in that state (may be empty).
- */
- public static final String ACTION_TETHER_STATE_CHANGED =
- "android.net.conn.TETHER_STATE_CHANGED";
-
- /**
- * gives a String[] listing all the interfaces configured for
- * tethering and currently available for tethering.
- */
- public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
-
- /**
- * gives a String[] listing all the interfaces currently in local-only
- * mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding)
- */
- public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
-
- /**
- * gives a String[] listing all the interfaces currently tethered
- * (ie, has DHCPv4 support and packets potentially forwarded/NATed)
- */
- public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
-
- /**
- * gives a String[] listing all the interfaces we tried to tether and
- * failed. Use {@link #getLastTetherError} to find the error code
- * for any interfaces listed here.
- */
- public static final String EXTRA_ERRORED_TETHER = "erroredArray";
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = false, value = {
- TETHERING_WIFI,
- TETHERING_USB,
- TETHERING_BLUETOOTH,
- TETHERING_WIFI_P2P,
- TETHERING_NCM,
- TETHERING_ETHERNET,
- })
- public @interface TetheringType {
- }
-
- /**
- * Invalid tethering type.
- * @see #startTethering.
- */
- public static final int TETHERING_INVALID = -1;
-
- /**
- * Wifi tethering type.
- * @see #startTethering.
- */
- public static final int TETHERING_WIFI = 0;
-
- /**
- * USB tethering type.
- * @see #startTethering.
- */
- public static final int TETHERING_USB = 1;
-
- /**
- * Bluetooth tethering type.
- * @see #startTethering.
- */
- public static final int TETHERING_BLUETOOTH = 2;
-
- /**
- * Wifi P2p tethering type.
- * Wifi P2p tethering is set through events automatically, and don't
- * need to start from #startTethering.
- */
- public static final int TETHERING_WIFI_P2P = 3;
-
- /**
- * Ncm local tethering type.
- * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback)
- */
- public static final int TETHERING_NCM = 4;
-
- /**
- * Ethernet tethering type.
- * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback)
- */
- public static final int TETHERING_ETHERNET = 5;
-
- /**
- * WIGIG tethering type. Use a separate type to prevent
- * conflicts with TETHERING_WIFI
- * This type is only used internally by the tethering module
- * @hide
- */
- public static final int TETHERING_WIGIG = 6;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- TETHER_ERROR_NO_ERROR,
- TETHER_ERROR_PROVISIONING_FAILED,
- TETHER_ERROR_ENTITLEMENT_UNKNOWN,
- })
- public @interface EntitlementResult {
- }
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- TETHER_ERROR_NO_ERROR,
- TETHER_ERROR_UNKNOWN_IFACE,
- TETHER_ERROR_SERVICE_UNAVAIL,
- TETHER_ERROR_INTERNAL_ERROR,
- TETHER_ERROR_TETHER_IFACE_ERROR,
- TETHER_ERROR_ENABLE_FORWARDING_ERROR,
- TETHER_ERROR_DISABLE_FORWARDING_ERROR,
- TETHER_ERROR_IFACE_CFG_ERROR,
- TETHER_ERROR_DHCPSERVER_ERROR,
- })
- public @interface TetheringIfaceError {
- }
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- TETHER_ERROR_SERVICE_UNAVAIL,
- TETHER_ERROR_INTERNAL_ERROR,
- TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION,
- TETHER_ERROR_UNKNOWN_TYPE,
- })
- public @interface StartTetheringError {
- }
-
- public static final int TETHER_ERROR_NO_ERROR = 0;
- public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
- public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
- public static final int TETHER_ERROR_UNSUPPORTED = 3;
- public static final int TETHER_ERROR_UNAVAIL_IFACE = 4;
- public static final int TETHER_ERROR_INTERNAL_ERROR = 5;
- public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
- public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
- public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8;
- public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9;
- public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
- public static final int TETHER_ERROR_PROVISIONING_FAILED = 11;
- public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
- public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13;
- public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14;
- public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15;
- public static final int TETHER_ERROR_UNKNOWN_TYPE = 16;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = false, value = {
- TETHER_HARDWARE_OFFLOAD_STOPPED,
- TETHER_HARDWARE_OFFLOAD_STARTED,
- TETHER_HARDWARE_OFFLOAD_FAILED,
- })
- public @interface TetherOffloadStatus {
- }
-
- /** Tethering offload status is stopped. */
- public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0;
- /** Tethering offload status is started. */
- public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1;
- /** Fail to start tethering offload. */
- public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2;
-
- /**
- * Create a TetheringManager object for interacting with the tethering service.
- *
- * @param context Context for the manager.
- * @param connectorSupplier Supplier for the manager connector; may return null while the
- * service is not connected.
- * {@hide}
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public TetheringManager(@NonNull final Context context,
- @NonNull Supplier<IBinder> connectorSupplier) {
- mContext = context;
- mCallback = new TetheringCallbackInternal();
- mConnectorSupplier = connectorSupplier;
-
- final String pkgName = mContext.getOpPackageName();
-
- final IBinder connector = mConnectorSupplier.get();
- // If the connector is available on start, do not start a polling thread. This introduces
- // differences in the thread that sends the oneway binder calls to the service between the
- // first few seconds after boot and later, but it avoids always having differences between
- // the first usage of TetheringManager from a process and subsequent usages (so the
- // difference is only on boot). On boot binder calls may be queued until the service comes
- // up and be sent from a worker thread; later, they are always sent from the caller thread.
- // Considering that it's just oneway binder calls, and ordering is preserved, this seems
- // better than inconsistent behavior persisting after boot.
- if (connector != null) {
- mConnector = ITetheringConnector.Stub.asInterface(connector);
- } else {
- startPollingForConnector();
- }
-
- Log.i(TAG, "registerTetheringEventCallback:" + pkgName);
- getConnector(c -> c.registerTetheringEventCallback(mCallback, pkgName));
- }
-
- private void startPollingForConnector() {
- new Thread(() -> {
- while (true) {
- try {
- Thread.sleep(CONNECTOR_POLL_INTERVAL_MILLIS);
- } catch (InterruptedException e) {
- // Not much to do here, the system needs to wait for the connector
- }
-
- final IBinder connector = mConnectorSupplier.get();
- if (connector != null) {
- onTetheringConnected(ITetheringConnector.Stub.asInterface(connector));
- return;
- }
- }
- }).start();
- }
-
- private interface ConnectorConsumer {
- void onConnectorAvailable(ITetheringConnector connector) throws RemoteException;
- }
-
- private void onTetheringConnected(ITetheringConnector connector) {
- // Process the connector wait queue in order, including any items that are added
- // while processing.
- //
- // 1. Copy the queue to a local variable under lock.
- // 2. Drain the local queue with the lock released (otherwise, enqueuing future commands
- // would block on the lock).
- // 3. Acquire the lock again. If any new tasks were queued during step 2, goto 1.
- // If not, set mConnector to non-null so future tasks are run immediately, not queued.
- //
- // For this to work, all calls to the tethering service must use getConnector(), which
- // ensures that tasks are added to the queue with the lock held.
- //
- // Once mConnector is set to non-null, it will never be null again. If the network stack
- // process crashes, no recovery is possible.
- // TODO: evaluate whether it is possible to recover from network stack process crashes
- // (though in most cases the system will have crashed when the network stack process
- // crashes).
- do {
- final List<ConnectorConsumer> localWaitQueue;
- synchronized (mConnectorWaitQueue) {
- localWaitQueue = new ArrayList<>(mConnectorWaitQueue);
- mConnectorWaitQueue.clear();
- }
-
- // Allow more tasks to be added at the end without blocking while draining the queue.
- for (ConnectorConsumer task : localWaitQueue) {
- try {
- task.onConnectorAvailable(connector);
- } catch (RemoteException e) {
- // Most likely the network stack process crashed, which is likely to crash the
- // system. Keep processing other requests but report the error loudly.
- Log.wtf(TAG, "Error processing request for the tethering connector", e);
- }
- }
-
- synchronized (mConnectorWaitQueue) {
- if (mConnectorWaitQueue.size() == 0) {
- mConnector = connector;
- return;
- }
- }
- } while (true);
- }
-
- /**
- * Asynchronously get the ITetheringConnector to execute some operation.
- *
- * <p>If the connector is already available, the operation will be executed on the caller's
- * thread. Otherwise it will be queued and executed on a worker thread. The operation should be
- * limited to performing oneway binder calls to minimize differences due to threading.
- */
- private void getConnector(ConnectorConsumer consumer) {
- final ITetheringConnector connector;
- synchronized (mConnectorWaitQueue) {
- connector = mConnector;
- if (connector == null) {
- mConnectorWaitQueue.add(consumer);
- return;
- }
- }
-
- try {
- consumer.onConnectorAvailable(connector);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- }
-
- private interface RequestHelper {
- void runRequest(ITetheringConnector connector, IIntResultListener listener);
- }
-
- // Used to dispatch legacy ConnectivityManager methods that expect tethering to be able to
- // return results and perform operations synchronously.
- // TODO: remove once there are no callers of these legacy methods.
- private class RequestDispatcher {
- private final ConditionVariable mWaiting;
- public volatile int mRemoteResult;
-
- private final IIntResultListener mListener = new IIntResultListener.Stub() {
- @Override
- public void onResult(final int resultCode) {
- mRemoteResult = resultCode;
- mWaiting.open();
- }
- };
-
- RequestDispatcher() {
- mWaiting = new ConditionVariable();
- }
-
- int waitForResult(final RequestHelper request) {
- getConnector(c -> request.runRequest(c, mListener));
- if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) {
- throw new IllegalStateException("Callback timeout");
- }
-
- throwIfPermissionFailure(mRemoteResult);
-
- return mRemoteResult;
- }
- }
-
- private void throwIfPermissionFailure(final int errorCode) {
- switch (errorCode) {
- case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:
- throw new SecurityException("No android.permission.TETHER_PRIVILEGED"
- + " or android.permission.WRITE_SETTINGS permission");
- case TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION:
- throw new SecurityException(
- "No android.permission.ACCESS_NETWORK_STATE permission");
- }
- }
-
- private class TetheringCallbackInternal extends ITetheringEventCallback.Stub {
- private volatile int mError = TETHER_ERROR_NO_ERROR;
- private final ConditionVariable mWaitForCallback = new ConditionVariable();
-
- @Override
- public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
- mTetheringConfiguration = parcel.config;
- mTetherStatesParcel = parcel.states;
- mWaitForCallback.open();
- }
-
- @Override
- public void onCallbackStopped(int errorCode) {
- mError = errorCode;
- mWaitForCallback.open();
- }
-
- @Override
- public void onUpstreamChanged(Network network) { }
-
- @Override
- public void onConfigurationChanged(TetheringConfigurationParcel config) {
- mTetheringConfiguration = config;
- }
-
- @Override
- public void onTetherStatesChanged(TetherStatesParcel states) {
- mTetherStatesParcel = states;
- }
-
- @Override
- public void onTetherClientsChanged(List<TetheredClient> clients) { }
-
- @Override
- public void onOffloadStatusChanged(int status) { }
-
- public void waitForStarted() {
- mWaitForCallback.block(DEFAULT_TIMEOUT_MS);
- throwIfPermissionFailure(mError);
- }
- }
-
- /**
- * Attempt to tether the named interface. This will setup a dhcp server
- * on the interface, forward and NAT IP v4 packets and forward DNS requests
- * to the best active upstream network interface. Note that if no upstream
- * IP network interface is available, dhcp will still run and traffic will be
- * allowed between the tethered devices and this device, though upstream net
- * access will of course fail until an upstream network interface becomes
- * active.
- *
- * @deprecated The only usages is PanService. It uses this for legacy reasons
- * and will migrate away as soon as possible.
- *
- * @param iface the interface name to tether.
- * @return error a {@code TETHER_ERROR} value indicating success or failure type
- *
- * {@hide}
- */
- @Deprecated
- @SystemApi(client = MODULE_LIBRARIES)
- public int tether(@NonNull final String iface) {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "tether caller:" + callerPkg);
- final RequestDispatcher dispatcher = new RequestDispatcher();
-
- return dispatcher.waitForResult((connector, listener) -> {
- try {
- connector.tether(iface, callerPkg, getAttributionTag(), listener);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- });
- }
-
- /**
- * @return the context's attribution tag
- */
- private @Nullable String getAttributionTag() {
- return mContext.getAttributionTag();
- }
-
- /**
- * Stop tethering the named interface.
- *
- * @deprecated The only usages is PanService. It uses this for legacy reasons
- * and will migrate away as soon as possible.
- *
- * {@hide}
- */
- @Deprecated
- @SystemApi(client = MODULE_LIBRARIES)
- public int untether(@NonNull final String iface) {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "untether caller:" + callerPkg);
-
- final RequestDispatcher dispatcher = new RequestDispatcher();
-
- return dispatcher.waitForResult((connector, listener) -> {
- try {
- connector.untether(iface, callerPkg, getAttributionTag(), listener);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- });
- }
-
- /**
- * Attempt to both alter the mode of USB and Tethering of USB.
- *
- * @deprecated New client should not use this API anymore. All clients should use
- * #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is
- * used and an entitlement check is needed, downstream USB tethering will be enabled but will
- * not have any upstream.
- *
- * {@hide}
- */
- @Deprecated
- @SystemApi(client = MODULE_LIBRARIES)
- public int setUsbTethering(final boolean enable) {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "setUsbTethering caller:" + callerPkg);
-
- final RequestDispatcher dispatcher = new RequestDispatcher();
-
- return dispatcher.waitForResult((connector, listener) -> {
- try {
- connector.setUsbTethering(enable, callerPkg, getAttributionTag(),
- listener);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- });
- }
-
- /**
- * Use with {@link #startTethering} to specify additional parameters when starting tethering.
- */
- public static class TetheringRequest {
- /** A configuration set for TetheringRequest. */
- private final TetheringRequestParcel mRequestParcel;
-
- private TetheringRequest(final TetheringRequestParcel request) {
- mRequestParcel = request;
- }
-
- /** Builder used to create TetheringRequest. */
- public static class Builder {
- private final TetheringRequestParcel mBuilderParcel;
-
- /** Default constructor of Builder. */
- public Builder(@TetheringType final int type) {
- mBuilderParcel = new TetheringRequestParcel();
- mBuilderParcel.tetheringType = type;
- mBuilderParcel.localIPv4Address = null;
- mBuilderParcel.staticClientAddress = null;
- mBuilderParcel.exemptFromEntitlementCheck = false;
- mBuilderParcel.showProvisioningUi = true;
- }
-
- /**
- * Configure tethering with static IPv4 assignment.
- *
- * A DHCP server will be started, but will only be able to offer the client address.
- * The two addresses must be in the same prefix.
- *
- * @param localIPv4Address The preferred local IPv4 link address to use.
- * @param clientAddress The static client address.
- */
- @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
- @NonNull
- public Builder setStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address,
- @NonNull final LinkAddress clientAddress) {
- Objects.requireNonNull(localIPv4Address);
- Objects.requireNonNull(clientAddress);
- if (!checkStaticAddressConfiguration(localIPv4Address, clientAddress)) {
- throw new IllegalArgumentException("Invalid server or client addresses");
- }
-
- mBuilderParcel.localIPv4Address = localIPv4Address;
- mBuilderParcel.staticClientAddress = clientAddress;
- return this;
- }
-
- /** Start tethering without entitlement checks. */
- @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
- @NonNull
- public Builder setExemptFromEntitlementCheck(boolean exempt) {
- mBuilderParcel.exemptFromEntitlementCheck = exempt;
- return this;
- }
-
- /**
- * If an entitlement check is needed, sets whether to show the entitlement UI or to
- * perform a silent entitlement check. By default, the entitlement UI is shown.
- */
- @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
- @NonNull
- public Builder setShouldShowEntitlementUi(boolean showUi) {
- mBuilderParcel.showProvisioningUi = showUi;
- return this;
- }
-
- /** Build {@link TetheringRequest] with the currently set configuration. */
- @NonNull
- public TetheringRequest build() {
- return new TetheringRequest(mBuilderParcel);
- }
- }
-
- /**
- * Get the local IPv4 address, if one was configured with
- * {@link Builder#setStaticIpv4Addresses}.
- */
- @Nullable
- public LinkAddress getLocalIpv4Address() {
- return mRequestParcel.localIPv4Address;
- }
-
- /**
- * Get the static IPv4 address of the client, if one was configured with
- * {@link Builder#setStaticIpv4Addresses}.
- */
- @Nullable
- public LinkAddress getClientStaticIpv4Address() {
- return mRequestParcel.staticClientAddress;
- }
-
- /** Get tethering type. */
- @TetheringType
- public int getTetheringType() {
- return mRequestParcel.tetheringType;
- }
-
- /** Check if exempt from entitlement check. */
- public boolean isExemptFromEntitlementCheck() {
- return mRequestParcel.exemptFromEntitlementCheck;
- }
-
- /** Check if show entitlement ui. */
- public boolean getShouldShowEntitlementUi() {
- return mRequestParcel.showProvisioningUi;
- }
-
- /**
- * Check whether the two addresses are ipv4 and in the same prefix.
- * @hide
- */
- public static boolean checkStaticAddressConfiguration(
- @NonNull final LinkAddress localIPv4Address,
- @NonNull final LinkAddress clientAddress) {
- return localIPv4Address.getPrefixLength() == clientAddress.getPrefixLength()
- && localIPv4Address.isIpv4() && clientAddress.isIpv4()
- && new IpPrefix(localIPv4Address.toString()).equals(
- new IpPrefix(clientAddress.toString()));
- }
-
- /**
- * Get a TetheringRequestParcel from the configuration
- * @hide
- */
- public TetheringRequestParcel getParcel() {
- return mRequestParcel;
- }
-
- /** String of TetheringRequest detail. */
- public String toString() {
- return "TetheringRequest [ type= " + mRequestParcel.tetheringType
- + ", localIPv4Address= " + mRequestParcel.localIPv4Address
- + ", staticClientAddress= " + mRequestParcel.staticClientAddress
- + ", exemptFromEntitlementCheck= "
- + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= "
- + mRequestParcel.showProvisioningUi + " ]";
- }
- }
-
- /**
- * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
- */
- public interface StartTetheringCallback {
- /**
- * Called when tethering has been successfully started.
- */
- default void onTetheringStarted() {}
-
- /**
- * Called when starting tethering failed.
- *
- * @param error The error that caused the failure.
- */
- default void onTetheringFailed(@StartTetheringError final int error) {}
- }
-
- /**
- * Starts tethering and runs tether provisioning for the given type if needed. If provisioning
- * fails, stopTethering will be called automatically.
- *
- * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
- * fail if a tethering entitlement check is required.
- *
- * @param request a {@link TetheringRequest} which can specify the preferred configuration.
- * @param executor {@link Executor} to specify the thread upon which the callback of
- * TetheringRequest will be invoked.
- * @param callback A callback that will be called to indicate the success status of the
- * tethering start request.
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.TETHER_PRIVILEGED,
- android.Manifest.permission.WRITE_SETTINGS
- })
- public void startTethering(@NonNull final TetheringRequest request,
- @NonNull final Executor executor, @NonNull final StartTetheringCallback callback) {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "startTethering caller:" + callerPkg);
-
- final IIntResultListener listener = new IIntResultListener.Stub() {
- @Override
- public void onResult(final int resultCode) {
- executor.execute(() -> {
- if (resultCode == TETHER_ERROR_NO_ERROR) {
- callback.onTetheringStarted();
- } else {
- callback.onTetheringFailed(resultCode);
- }
- });
- }
- };
- getConnector(c -> c.startTethering(request.getParcel(), callerPkg,
- getAttributionTag(), listener));
- }
-
- /**
- * Starts tethering and runs tether provisioning for the given type if needed. If provisioning
- * fails, stopTethering will be called automatically.
- *
- * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
- * fail if a tethering entitlement check is required.
- *
- * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.
- * @param executor {@link Executor} to specify the thread upon which the callback of
- * TetheringRequest will be invoked.
- * @hide
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.TETHER_PRIVILEGED,
- android.Manifest.permission.WRITE_SETTINGS
- })
- @SystemApi(client = MODULE_LIBRARIES)
- public void startTethering(int type, @NonNull final Executor executor,
- @NonNull final StartTetheringCallback callback) {
- startTethering(new TetheringRequest.Builder(type).build(), executor, callback);
- }
-
- /**
- * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
- * applicable.
- *
- * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
- * fail if a tethering entitlement check is required.
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.TETHER_PRIVILEGED,
- android.Manifest.permission.WRITE_SETTINGS
- })
- public void stopTethering(@TetheringType final int type) {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "stopTethering caller:" + callerPkg);
-
- getConnector(c -> c.stopTethering(type, callerPkg, getAttributionTag(),
- new IIntResultListener.Stub() {
- @Override
- public void onResult(int resultCode) {
- // TODO: provide an API to obtain result
- // This has never been possible as stopTethering has always been void and never
- // taken a callback object. The only indication that callers have is if the call
- // results in a TETHER_STATE_CHANGE broadcast.
- }
- }));
- }
-
- /**
- * Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether
- * entitlement succeeded.
- */
- public interface OnTetheringEntitlementResultListener {
- /**
- * Called to notify entitlement result.
- *
- * @param resultCode an int value of entitlement result. It may be one of
- * {@link #TETHER_ERROR_NO_ERROR},
- * {@link #TETHER_ERROR_PROVISIONING_FAILED}, or
- * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}.
- */
- void onTetheringEntitlementResult(@EntitlementResult int result);
- }
-
- /**
- * Request the latest value of the tethering entitlement check.
- *
- * <p>This method will only return the latest entitlement result if it is available. If no
- * cached entitlement result is available, and {@code showEntitlementUi} is false,
- * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN} will be returned. If {@code showEntitlementUi} is
- * true, entitlement will be run.
- *
- * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
- * fail if a tethering entitlement check is required.
- *
- * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants.
- * @param showEntitlementUi a boolean indicating whether to check result for the UI-based
- * entitlement check or the silent entitlement check.
- * @param executor the executor on which callback will be invoked.
- * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to
- * notify the caller of the result of entitlement check. The listener may be called zero
- * or one time.
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.TETHER_PRIVILEGED,
- android.Manifest.permission.WRITE_SETTINGS
- })
- public void requestLatestTetheringEntitlementResult(@TetheringType int type,
- boolean showEntitlementUi,
- @NonNull Executor executor,
- @NonNull final OnTetheringEntitlementResultListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException(
- "OnTetheringEntitlementResultListener cannot be null.");
- }
-
- ResultReceiver wrappedListener = new ResultReceiver(null /* handler */) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- executor.execute(() -> {
- listener.onTetheringEntitlementResult(resultCode);
- });
- }
- };
-
- requestLatestTetheringEntitlementResult(type, wrappedListener,
- showEntitlementUi);
- }
-
- /**
- * Helper function of #requestLatestTetheringEntitlementResult to remain backwards compatible
- * with ConnectivityManager#getLatestTetheringEntitlementResult
- *
- * {@hide}
- */
- // TODO: improve the usage of ResultReceiver, b/145096122
- @SystemApi(client = MODULE_LIBRARIES)
- public void requestLatestTetheringEntitlementResult(@TetheringType final int type,
- @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg);
-
- getConnector(c -> c.requestLatestTetheringEntitlementResult(
- type, receiver, showEntitlementUi, callerPkg, getAttributionTag()));
- }
-
- /**
- * Callback for use with {@link registerTetheringEventCallback} to find out tethering
- * upstream status.
- */
- public interface TetheringEventCallback {
- /**
- * Called when tethering supported status changed.
- *
- * <p>This will be called immediately after the callback is registered, and may be called
- * multiple times later upon changes.
- *
- * <p>Tethering may be disabled via system properties, device configuration, or device
- * policy restrictions.
- *
- * @param supported The new supported status
- */
- default void onTetheringSupported(boolean supported) {}
-
- /**
- * Called when tethering upstream changed.
- *
- * <p>This will be called immediately after the callback is registered, and may be called
- * multiple times later upon changes.
- *
- * @param network the {@link Network} of tethering upstream. Null means tethering doesn't
- * have any upstream.
- */
- default void onUpstreamChanged(@Nullable Network network) {}
-
- /**
- * Called when there was a change in tethering interface regular expressions.
- *
- * <p>This will be called immediately after the callback is registered, and may be called
- * multiple times later upon changes.
- * @param reg The new regular expressions.
- *
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
-
- /**
- * Called when there was a change in the list of tetherable interfaces. Tetherable
- * interface means this interface is available and can be used for tethering.
- *
- * <p>This will be called immediately after the callback is registered, and may be called
- * multiple times later upon changes.
- * @param interfaces The list of tetherable interface names.
- */
- default void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
-
- /**
- * Called when there was a change in the list of tethered interfaces.
- *
- * <p>This will be called immediately after the callback is registered, and may be called
- * multiple times later upon changes.
- * @param interfaces The list of 0 or more String of currently tethered interface names.
- */
- default void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
-
- /**
- * Called when an error occurred configuring tethering.
- *
- * <p>This will be called immediately after the callback is registered if the latest status
- * on the interface is an error, and may be called multiple times later upon changes.
- * @param ifName Name of the interface.
- * @param error One of {@code TetheringManager#TETHER_ERROR_*}.
- */
- default void onError(@NonNull String ifName, @TetheringIfaceError int error) {}
-
- /**
- * Called when the list of tethered clients changes.
- *
- * <p>This callback provides best-effort information on connected clients based on state
- * known to the system, however the list cannot be completely accurate (and should not be
- * used for security purposes). For example, clients behind a bridge and using static IP
- * assignments are not visible to the tethering device; or even when using DHCP, such
- * clients may still be reported by this callback after disconnection as the system cannot
- * determine if they are still connected.
- * @param clients The new set of tethered clients; the collection is not ordered.
- */
- default void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
-
- /**
- * Called when tethering offload status changes.
- *
- * <p>This will be called immediately after the callback is registered.
- * @param status The offload status.
- */
- default void onOffloadStatusChanged(@TetherOffloadStatus int status) {}
- }
-
- /**
- * Regular expressions used to identify tethering interfaces.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public static class TetheringInterfaceRegexps {
- private final String[] mTetherableBluetoothRegexs;
- private final String[] mTetherableUsbRegexs;
- private final String[] mTetherableWifiRegexs;
-
- /** @hide */
- public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs,
- @NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) {
- mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone();
- mTetherableUsbRegexs = tetherableUsbRegexs.clone();
- mTetherableWifiRegexs = tetherableWifiRegexs.clone();
- }
-
- @NonNull
- public List<String> getTetherableBluetoothRegexs() {
- return Collections.unmodifiableList(Arrays.asList(mTetherableBluetoothRegexs));
- }
-
- @NonNull
- public List<String> getTetherableUsbRegexs() {
- return Collections.unmodifiableList(Arrays.asList(mTetherableUsbRegexs));
- }
-
- @NonNull
- public List<String> getTetherableWifiRegexs() {
- return Collections.unmodifiableList(Arrays.asList(mTetherableWifiRegexs));
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mTetherableBluetoothRegexs, mTetherableUsbRegexs,
- mTetherableWifiRegexs);
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (!(obj instanceof TetheringInterfaceRegexps)) return false;
- final TetheringInterfaceRegexps other = (TetheringInterfaceRegexps) obj;
- return Arrays.equals(mTetherableBluetoothRegexs, other.mTetherableBluetoothRegexs)
- && Arrays.equals(mTetherableUsbRegexs, other.mTetherableUsbRegexs)
- && Arrays.equals(mTetherableWifiRegexs, other.mTetherableWifiRegexs);
- }
- }
-
- /**
- * Start listening to tethering change events. Any new added callback will receive the last
- * tethering status right away. If callback is registered,
- * {@link TetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering
- * has no upstream or disabled, the argument of callback will be null. The same callback object
- * cannot be registered twice.
- *
- * @param executor the executor on which callback will be invoked.
- * @param callback the callback to be called when tethering has change events.
- */
- @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
- public void registerTetheringEventCallback(@NonNull Executor executor,
- @NonNull TetheringEventCallback callback) {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg);
-
- synchronized (mTetheringEventCallbacks) {
- if (mTetheringEventCallbacks.containsKey(callback)) {
- throw new IllegalArgumentException("callback was already registered.");
- }
- final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() {
- // Only accessed with a lock on this object
- private final HashMap<String, Integer> mErrorStates = new HashMap<>();
- private String[] mLastTetherableInterfaces = null;
- private String[] mLastTetheredInterfaces = null;
-
- @Override
- public void onUpstreamChanged(Network network) throws RemoteException {
- executor.execute(() -> {
- callback.onUpstreamChanged(network);
- });
- }
-
- private synchronized void sendErrorCallbacks(final TetherStatesParcel newStates) {
- for (int i = 0; i < newStates.erroredIfaceList.length; i++) {
- final String iface = newStates.erroredIfaceList[i];
- final Integer lastError = mErrorStates.get(iface);
- final int newError = newStates.lastErrorList[i];
- if (newError != TETHER_ERROR_NO_ERROR
- && !Objects.equals(lastError, newError)) {
- callback.onError(iface, newError);
- }
- mErrorStates.put(iface, newError);
- }
- }
-
- private synchronized void maybeSendTetherableIfacesChangedCallback(
- final TetherStatesParcel newStates) {
- if (Arrays.equals(mLastTetherableInterfaces, newStates.availableList)) return;
- mLastTetherableInterfaces = newStates.availableList.clone();
- callback.onTetherableInterfacesChanged(
- Collections.unmodifiableList(Arrays.asList(mLastTetherableInterfaces)));
- }
-
- private synchronized void maybeSendTetheredIfacesChangedCallback(
- final TetherStatesParcel newStates) {
- if (Arrays.equals(mLastTetheredInterfaces, newStates.tetheredList)) return;
- mLastTetheredInterfaces = newStates.tetheredList.clone();
- callback.onTetheredInterfacesChanged(
- Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces)));
- }
-
- // Called immediately after the callbacks are registered.
- @Override
- public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
- executor.execute(() -> {
- callback.onTetheringSupported(parcel.tetheringSupported);
- callback.onUpstreamChanged(parcel.upstreamNetwork);
- sendErrorCallbacks(parcel.states);
- sendRegexpsChanged(parcel.config);
- maybeSendTetherableIfacesChangedCallback(parcel.states);
- maybeSendTetheredIfacesChangedCallback(parcel.states);
- callback.onClientsChanged(parcel.tetheredClients);
- callback.onOffloadStatusChanged(parcel.offloadStatus);
- });
- }
-
- @Override
- public void onCallbackStopped(int errorCode) {
- executor.execute(() -> {
- throwIfPermissionFailure(errorCode);
- });
- }
-
- private void sendRegexpsChanged(TetheringConfigurationParcel parcel) {
- callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps(
- parcel.tetherableBluetoothRegexs,
- parcel.tetherableUsbRegexs,
- parcel.tetherableWifiRegexs));
- }
-
- @Override
- public void onConfigurationChanged(TetheringConfigurationParcel config) {
- executor.execute(() -> sendRegexpsChanged(config));
- }
-
- @Override
- public void onTetherStatesChanged(TetherStatesParcel states) {
- executor.execute(() -> {
- sendErrorCallbacks(states);
- maybeSendTetherableIfacesChangedCallback(states);
- maybeSendTetheredIfacesChangedCallback(states);
- });
- }
-
- @Override
- public void onTetherClientsChanged(final List<TetheredClient> clients) {
- executor.execute(() -> callback.onClientsChanged(clients));
- }
-
- @Override
- public void onOffloadStatusChanged(final int status) {
- executor.execute(() -> callback.onOffloadStatusChanged(status));
- }
- };
- getConnector(c -> c.registerTetheringEventCallback(remoteCallback, callerPkg));
- mTetheringEventCallbacks.put(callback, remoteCallback);
- }
- }
-
- /**
- * Remove tethering event callback previously registered with
- * {@link #registerTetheringEventCallback}.
- *
- * @param callback previously registered callback.
- */
- @RequiresPermission(anyOf = {
- Manifest.permission.TETHER_PRIVILEGED,
- Manifest.permission.ACCESS_NETWORK_STATE
- })
- public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg);
-
- synchronized (mTetheringEventCallbacks) {
- ITetheringEventCallback remoteCallback = mTetheringEventCallbacks.remove(callback);
- if (remoteCallback == null) {
- throw new IllegalArgumentException("callback was not registered.");
- }
-
- getConnector(c -> c.unregisterTetheringEventCallback(remoteCallback, callerPkg));
- }
- }
-
- /**
- * Get a more detailed error code after a Tethering or Untethering
- * request asynchronously failed.
- *
- * @param iface The name of the interface of interest
- * @return error The error code of the last error tethering or untethering the named
- * interface
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public int getLastTetherError(@NonNull final String iface) {
- mCallback.waitForStarted();
- if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
-
- int i = 0;
- for (String errored : mTetherStatesParcel.erroredIfaceList) {
- if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i];
-
- i++;
- }
- return TETHER_ERROR_NO_ERROR;
- }
-
- /**
- * Get the list of regular expressions that define any tetherable
- * USB network interfaces. If USB tethering is not supported by the
- * device, this list should be empty.
- *
- * @return an array of 0 or more regular expression Strings defining
- * what interfaces are considered tetherable usb interfaces.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public @NonNull String[] getTetherableUsbRegexs() {
- mCallback.waitForStarted();
- return mTetheringConfiguration.tetherableUsbRegexs;
- }
-
- /**
- * Get the list of regular expressions that define any tetherable
- * Wifi network interfaces. If Wifi tethering is not supported by the
- * device, this list should be empty.
- *
- * @return an array of 0 or more regular expression Strings defining
- * what interfaces are considered tetherable wifi interfaces.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public @NonNull String[] getTetherableWifiRegexs() {
- mCallback.waitForStarted();
- return mTetheringConfiguration.tetherableWifiRegexs;
- }
-
- /**
- * Get the list of regular expressions that define any tetherable
- * Bluetooth network interfaces. If Bluetooth tethering is not supported by the
- * device, this list should be empty.
- *
- * @return an array of 0 or more regular expression Strings defining
- * what interfaces are considered tetherable bluetooth interfaces.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public @NonNull String[] getTetherableBluetoothRegexs() {
- mCallback.waitForStarted();
- return mTetheringConfiguration.tetherableBluetoothRegexs;
- }
-
- /**
- * Get the set of tetherable, available interfaces. This list is limited by
- * device configuration and current interface existence.
- *
- * @return an array of 0 or more Strings of tetherable interface names.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public @NonNull String[] getTetherableIfaces() {
- mCallback.waitForStarted();
- if (mTetherStatesParcel == null) return new String[0];
-
- return mTetherStatesParcel.availableList;
- }
-
- /**
- * Get the set of tethered interfaces.
- *
- * @return an array of 0 or more String of currently tethered interface names.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public @NonNull String[] getTetheredIfaces() {
- mCallback.waitForStarted();
- if (mTetherStatesParcel == null) return new String[0];
-
- return mTetherStatesParcel.tetheredList;
- }
-
- /**
- * Get the set of interface names which attempted to tether but
- * failed. Re-attempting to tether may cause them to reset to the Tethered
- * state. Alternatively, causing the interface to be destroyed and recreated
- * may cause them to reset to the available state.
- * {@link TetheringManager#getLastTetherError} can be used to get more
- * information on the cause of the errors.
- *
- * @return an array of 0 or more String indicating the interface names
- * which failed to tether.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public @NonNull String[] getTetheringErroredIfaces() {
- mCallback.waitForStarted();
- if (mTetherStatesParcel == null) return new String[0];
-
- return mTetherStatesParcel.erroredIfaceList;
- }
-
- /**
- * Get the set of tethered dhcp ranges.
- *
- * @deprecated This API just return the default value which is not used in DhcpServer.
- * @hide
- */
- @Deprecated
- public @NonNull String[] getTetheredDhcpRanges() {
- mCallback.waitForStarted();
- return mTetheringConfiguration.legacyDhcpRanges;
- }
-
- /**
- * Check if the device allows for tethering. It may be disabled via
- * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
- * due to device configuration.
- *
- * @return a boolean - {@code true} indicating Tethering is supported.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public boolean isTetheringSupported() {
- final String callerPkg = mContext.getOpPackageName();
-
- return isTetheringSupported(callerPkg);
- }
-
- /**
- * Check if the device allows for tethering. It may be disabled via {@code ro.tether.denied}
- * system property, Settings.TETHER_SUPPORTED or due to device configuration. This is useful
- * for system components that query this API on behalf of an app. In particular, Bluetooth
- * has @UnsupportedAppUsage calls that will let apps turn on bluetooth tethering if they have
- * the right permissions, but such an app needs to know whether it can (permissions as well
- * as support from the device) turn on tethering in the first place to show the appropriate UI.
- *
- * @param callerPkg The caller package name, if it is not matching the calling uid,
- * SecurityException would be thrown.
- * @return a boolean - {@code true} indicating Tethering is supported.
- * @hide
- */
- @SystemApi(client = MODULE_LIBRARIES)
- public boolean isTetheringSupported(@NonNull final String callerPkg) {
-
- final RequestDispatcher dispatcher = new RequestDispatcher();
- final int ret = dispatcher.waitForResult((connector, listener) -> {
- try {
- connector.isTetheringSupported(callerPkg, getAttributionTag(), listener);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- });
-
- return ret == TETHER_ERROR_NO_ERROR;
- }
-
- /**
- * Stop all active tethering.
- *
- * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
- * fail if a tethering entitlement check is required.
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.TETHER_PRIVILEGED,
- android.Manifest.permission.WRITE_SETTINGS
- })
- public void stopAllTethering() {
- final String callerPkg = mContext.getOpPackageName();
- Log.i(TAG, "stopAllTethering caller:" + callerPkg);
-
- getConnector(c -> c.stopAllTethering(callerPkg, getAttributionTag(),
- new IIntResultListener.Stub() {
- @Override
- public void onResult(int resultCode) {
- // TODO: add an API parameter to send result to caller.
- // This has never been possible as stopAllTethering has always been void
- // and never taken a callback object. The only indication that callers have
- // is if the call results in a TETHER_STATE_CHANGE broadcast.
- }
- }));
- }
-}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
deleted file mode 100644
index c0280d3dbfaf..000000000000
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.net.LinkAddress;
-
-/**
- * Configuration details for requesting tethering.
- * @hide
- */
-parcelable TetheringRequestParcel {
- int tetheringType;
- LinkAddress localIPv4Address;
- LinkAddress staticClientAddress;
- boolean exemptFromEntitlementCheck;
- boolean showProvisioningUi;
-}
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
deleted file mode 100644
index 591861f5b837..000000000000
--- a/packages/Tethering/jarjar-rules.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# These must be kept in sync with the framework-tethering-shared-srcs filegroup.
-# Classes from the framework-tethering-shared-srcs filegroup.
-# If there are files in that filegroup that are not covered below, the classes in the
-# module will be overwritten by the ones in the framework.
-rule com.android.internal.util.** com.android.networkstack.tethering.util.@1
-rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
-
-rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1
-
-# Classes from net-utils-framework-common
-rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1 \ No newline at end of file
diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
deleted file mode 100644
index 94c871d8a366..000000000000
--- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <error.h>
-#include <jni.h>
-#include <linux/filter.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/JNIHelpCompat.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <net/if.h>
-#include <netinet/ether.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <sys/socket.h>
-#include <stdio.h>
-
-#define LOG_TAG "TetheringUtils"
-#include <android/log.h>
-
-namespace android {
-
-static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt);
-static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr);
-static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
-
-static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) {
- sock_filter filter_code[] = {
- // Check header is ICMPv6.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
-
- // Check ICMPv6 type.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
-
- const sock_fprog filter = {
- sizeof(filter_code) / sizeof(filter_code[0]),
- filter_code,
- };
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
- }
-}
-
-static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd)
-{
- android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
-}
-
-static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd)
-{
- android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
-}
-
-static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
- jint ifIndex)
-{
- static const int kLinkLocalHopLimit = 255;
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
-
- // Set an ICMPv6 filter that only passes Router Solicitations.
- struct icmp6_filter rs_only;
- ICMP6_FILTER_SETBLOCKALL(&rs_only);
- ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
- socklen_t len = sizeof(rs_only);
- if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(ICMP6_FILTER): %s", strerror(errno));
- return;
- }
-
- // Most/all of the rest of these options can be set via Java code, but
- // because we're here on account of setting an icmp6_filter go ahead
- // and do it all natively for now.
-
- // Set the multicast hoplimit to 255 (link-local only).
- int hops = kLinkLocalHopLimit;
- len = sizeof(hops);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
- return;
- }
-
- // Set the unicast hoplimit to 255 (link-local only).
- hops = kLinkLocalHopLimit;
- len = sizeof(hops);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
- return;
- }
-
- // Explicitly disable multicast loopback.
- int off = 0;
- len = sizeof(off);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
- return;
- }
-
- // Specify the IPv6 interface to use for outbound multicast.
- len = sizeof(ifIndex);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
- return;
- }
-
- // Additional options to be considered:
- // - IPV6_TCLASS
- // - IPV6_RECVPKTINFO
- // - IPV6_RECVHOPLIMIT
-
- // Bind to [::].
- const struct sockaddr_in6 sin6 = {
- .sin6_family = AF_INET6,
- .sin6_port = 0,
- .sin6_flowinfo = 0,
- .sin6_addr = IN6ADDR_ANY_INIT,
- .sin6_scope_id = 0,
- };
- auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
- len = sizeof(sin6);
- if (bind(fd, sa, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "bind(IN6ADDR_ANY): %s", strerror(errno));
- return;
- }
-
- // Join the all-routers multicast group, ff02::2%index.
- struct ipv6_mreq all_rtrs = {
- .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
- .ipv6mr_interface = ifIndex,
- };
- len = sizeof(all_rtrs);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
- return;
- }
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "setupNaSocket", "(Ljava/io/FileDescriptor;)V",
- (void*) android_net_util_setupNaSocket },
- { "setupNsSocket", "(Ljava/io/FileDescriptor;)V",
- (void*) android_net_util_setupNsSocket },
- { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V",
- (void*) android_net_util_setupRaSocket },
-};
-
-int register_android_net_util_TetheringUtils(JNIEnv* env) {
- return jniRegisterNativeMethods(env,
- "android/net/util/TetheringUtils",
- gMethods, NELEM(gMethods));
-}
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
- JNIEnv *env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
- return JNI_ERR;
- }
-
- if (register_android_net_util_TetheringUtils(env) < 0) {
- return JNI_ERR;
- }
-
- return JNI_VERSION_1_6;
-}
-
-}; // namespace android
diff --git a/packages/Tethering/proguard.flags b/packages/Tethering/proguard.flags
deleted file mode 100644
index 86b903353cf5..000000000000
--- a/packages/Tethering/proguard.flags
+++ /dev/null
@@ -1,9 +0,0 @@
-# Keep class's integer static field for MessageUtils to parsing their name.
--keep class com.android.networkstack.tethering.Tethering$TetherMainSM {
- static final int CMD_*;
- static final int EVENT_*;
-}
-
--keepclassmembers class android.net.ip.IpServer {
- static final int CMD_*;
-}
diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png
deleted file mode 100644
index 9451174d65d7..000000000000
--- a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png
deleted file mode 100644
index 79d5756ae38e..000000000000
--- a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png
deleted file mode 100644
index cae1bd1b2574..000000000000
--- a/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png
deleted file mode 100644
index ffe8e8c98232..000000000000
--- a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png
deleted file mode 100644
index ca20f7352090..000000000000
--- a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png
deleted file mode 100644
index 65e907565ec1..000000000000
--- a/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png
deleted file mode 100644
index f42dae0fdcb9..000000000000
--- a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png
deleted file mode 100644
index 065516185ad4..000000000000
--- a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png
deleted file mode 100644
index 2e2b8ca2e9cb..000000000000
--- a/packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
deleted file mode 100644
index 3f57d1c76ccb..000000000000
--- a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png
deleted file mode 100644
index 34b0cb36736a..000000000000
--- a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png
deleted file mode 100644
index 36afe485b5bb..000000000000
--- a/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
deleted file mode 100644
index 25acfbb01ba4..000000000000
--- a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png
deleted file mode 100644
index 5c656012e615..000000000000
--- a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png
deleted file mode 100644
index 28b4b5438e55..000000000000
--- a/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png
+++ /dev/null
Binary files differ
diff --git a/packages/Tethering/res/values-af/strings.xml b/packages/Tethering/res/values-af/strings.xml
deleted file mode 100644
index 056168b12e89..000000000000
--- a/packages/Tethering/res/values-af/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Verbinding of warmkol is aktief"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tik om op te stel."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Verbinding is gedeaktiveer"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontak jou administrateur vir besonderhede"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Warmkol- en verbindingstatus"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-am/strings.xml b/packages/Tethering/res/values-am/strings.xml
deleted file mode 100644
index ac468dd14414..000000000000
--- a/packages/Tethering/res/values-am/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"እንደ ሞደም መሰካት ወይም መገናኛ ነጥብ ገባሪ"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"ለማዋቀር መታ ያድርጉ።"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"እንደ ሞደም መሰካት ተሰናክሏል"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"መገናኛ ነጥብ እና እንደ ሞደም የመሰካት ሁኔታ"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ar/strings.xml b/packages/Tethering/res/values-ar/strings.xml
deleted file mode 100644
index 7d5bad34da04..000000000000
--- a/packages/Tethering/res/values-ar/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"النطاق نشط أو نقطة الاتصال نشطة"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"انقر للإعداد."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"التوصيل متوقف."</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"تواصَل مع المشرف للحصول على التفاصيل."</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"حالة نقطة الاتصال والتوصيل"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-as/strings.xml b/packages/Tethering/res/values-as/strings.xml
deleted file mode 100644
index 091350455ba0..000000000000
--- a/packages/Tethering/res/values-as/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"টে\'ডাৰিং অথবা হ\'টস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"ছেট আপ কৰিবলৈ টিপক।"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"টে\'ডাৰিঙৰ সুবিধাটো অক্ষম কৰি থোৱা হৈছে"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"হ’টস্প\'ট আৰু টে\'ডাৰিঙৰ স্থিতি"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-az/strings.xml b/packages/Tethering/res/values-az/strings.xml
deleted file mode 100644
index dce70da178f1..000000000000
--- a/packages/Tethering/res/values-az/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Birləşmə və ya hotspot aktivdir"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamaq üçün toxunun."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Birləşmə deaktivdir"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Detallar üçün adminlə əlaqə saxlayın"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot &amp; birləşmə statusu"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-b+sr+Latn/strings.xml b/packages/Tethering/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index b0774ec9a840..000000000000
--- a/packages/Tethering/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Privezivanje ili hotspot je aktivan"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste podesili."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Privezivanje je onemogućeno"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Potražite detalje od administratora"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspota i privezivanja"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-be/strings.xml b/packages/Tethering/res/values-be/strings.xml
deleted file mode 100644
index a8acebe2e992..000000000000
--- a/packages/Tethering/res/values-be/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Мадэм або хот-спот актыўныя"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Дакраніцеся, каб наладзіць."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Рэжым мадэма выключаны"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Стан \"Хот-спот і мадэм\""</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-bg/strings.xml b/packages/Tethering/res/values-bg/strings.xml
deleted file mode 100644
index 94fb2d8f176a..000000000000
--- a/packages/Tethering/res/values-bg/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Има активна споделена връзка или точка за достъп"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Докоснете, за да настроите."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Функцията за тетъринг е деактивирана"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Свържете се с администратора си за подробности"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Състояние на функцията за точка за достъп и тетъринг"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-bn/strings.xml b/packages/Tethering/res/values-bn/strings.xml
deleted file mode 100644
index aea02b9ddff8..000000000000
--- a/packages/Tethering/res/values-bn/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"টিথারিং বা হটস্পট চালু আছে"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"সেট-আপ করতে ট্যাপ করুন।"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"টিথারিং বন্ধ করা আছে"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"বিশদে জানতে অ্যাডমিনের সাথে যোগাযোগ করুন"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"হটস্পট ও টিথারিং স্ট্যাটাস"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-bs/strings.xml b/packages/Tethering/res/values-bs/strings.xml
deleted file mode 100644
index de232724c5d9..000000000000
--- a/packages/Tethering/res/values-bs/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Aktivno je povezivanje putem mobitela ili pristupna tačka"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da postavite."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezivanje putem mobitela je onemogućeno"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontaktirajte svog administratora za detalje"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status pristupne tačke i povezivanja putem mobitela"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ca/strings.xml b/packages/Tethering/res/values-ca/strings.xml
deleted file mode 100644
index 88b795c1f8b2..000000000000
--- a/packages/Tethering/res/values-ca/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Compartició de xarxa o punt d\'accés Wi‑Fi actius"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Toca per configurar."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"La compartició de xarxa està desactivada"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta amb el teu administrador per obtenir més informació"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estat del punt d\'accés Wi‑Fi i de la compartició de xarxa"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-cs/strings.xml b/packages/Tethering/res/values-cs/strings.xml
deleted file mode 100644
index 8c1b83bf3ee3..000000000000
--- a/packages/Tethering/res/values-cs/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering nebo hotspot je aktivní"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím zahájíte nastavení."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je zakázán"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požádejte administrátora"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-da/strings.xml b/packages/Tethering/res/values-da/strings.xml
deleted file mode 100644
index f413e7054819..000000000000
--- a/packages/Tethering/res/values-da/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Netdeling eller hotspot er aktivt"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tryk for at konfigurere."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Netdeling er deaktiveret"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakt din administrator for at få oplysninger"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for hotspot og netdeling"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-de/strings.xml b/packages/Tethering/res/values-de/strings.xml
deleted file mode 100644
index f057d7824e81..000000000000
--- a/packages/Tethering/res/values-de/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering oder Hotspot aktiv"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Zum Einrichten tippen."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering ist deaktiviert"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Bitte wende dich für weitere Informationen an den Administrator"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot- und Tethering-Status"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-el/strings.xml b/packages/Tethering/res/values-el/strings.xml
deleted file mode 100644
index b3c986bdafd6..000000000000
--- a/packages/Tethering/res/values-el/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Πατήστε για ρύθμιση."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Η σύνδεση είναι απενεργοποιημένη"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Κατάσταση σημείου πρόσβασης Wi-Fi και σύνδεσης"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-en-rAU/strings.xml b/packages/Tethering/res/values-en-rAU/strings.xml
deleted file mode 100644
index 769e01208afc..000000000000
--- a/packages/Tethering/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-en-rCA/strings.xml b/packages/Tethering/res/values-en-rCA/strings.xml
deleted file mode 100644
index 769e01208afc..000000000000
--- a/packages/Tethering/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-en-rGB/strings.xml b/packages/Tethering/res/values-en-rGB/strings.xml
deleted file mode 100644
index 769e01208afc..000000000000
--- a/packages/Tethering/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-en-rIN/strings.xml b/packages/Tethering/res/values-en-rIN/strings.xml
deleted file mode 100644
index 769e01208afc..000000000000
--- a/packages/Tethering/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-en-rXC/strings.xml b/packages/Tethering/res/values-en-rXC/strings.xml
deleted file mode 100644
index f1674bed4eb7..000000000000
--- a/packages/Tethering/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‎Tethering or hotspot active‎‏‎‎‏‎"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎Tap to set up.‎‏‎‎‏‎"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎Tethering is disabled‎‏‎‎‏‎"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎Contact your admin for details‎‏‎‎‏‎"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎Hotspot &amp; tethering status‎‏‎‎‏‎"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-es-rUS/strings.xml b/packages/Tethering/res/values-es-rUS/strings.xml
deleted file mode 100644
index 63689f43997c..000000000000
--- a/packages/Tethering/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión a red o hotspot conectados"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Presiona para configurar esta opción."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Se inhabilitó la conexión mediante dispositivo portátil"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Para obtener más información, comunícate con el administrador"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del hotspot y la conexión mediante dispositivo portátil"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-es/strings.xml b/packages/Tethering/res/values-es/strings.xml
deleted file mode 100644
index 9a34ed5e388a..000000000000
--- a/packages/Tethering/res/values-es/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida o punto de acceso activos"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"La conexión compartida está inhabilitada"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Solicita más información a tu administrador"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del punto de acceso y de la conexión compartida"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-et/strings.xml b/packages/Tethering/res/values-et/strings.xml
deleted file mode 100644
index 0970341ab0cd..000000000000
--- a/packages/Tethering/res/values-et/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Jagamine või kuumkoht on aktiivne"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Puudutage seadistamiseks."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Jagamine on keelatud"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Kuumkoha ja jagamise olek"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-eu/strings.xml b/packages/Tethering/res/values-eu/strings.xml
deleted file mode 100644
index 632019e2ef1b..000000000000
--- a/packages/Tethering/res/values-eu/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Konexioa partekatzea edo wifi-gunea aktibo dago"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Sakatu konfiguratzeko."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Desgaituta dago konexioa partekatzeko aukera"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Wifi-gunearen eta konexioa partekatzeko eginbidearen egoera"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-fa/strings.xml b/packages/Tethering/res/values-fa/strings.xml
deleted file mode 100644
index 2e21c85fa179..000000000000
--- a/packages/Tethering/res/values-fa/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"اشتراک‌گذاری اینترنت یا نقطه اتصال فعال"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"برای راه‌اندازی ضربه بزنید."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"اشتراک‌گذاری اینترنت غیرفعال است"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"برای جزئیات، با سرپرستتان تماس بگیرید"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"وضعیت نقطه اتصال و اشتراک‌گذاری اینترنت"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-fi/strings.xml b/packages/Tethering/res/values-fi/strings.xml
deleted file mode 100644
index 413db3f0f8c9..000000000000
--- a/packages/Tethering/res/values-fi/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Yhteyden jakaminen tai hotspot käytössä"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Ota käyttöön napauttamalla."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Yhteyden jakaminen on poistettu käytöstä"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Pyydä lisätietoja järjestelmänvalvojalta"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspotin ja yhteyden jakamisen tila"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-fr-rCA/strings.xml b/packages/Tethering/res/values-fr-rCA/strings.xml
deleted file mode 100644
index eb2e4ba54000..000000000000
--- a/packages/Tethering/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès sans fil activé"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Touchez pour configurer."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Communiquez avec votre administrateur pour obtenir plus de détails"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Point d\'accès et partage de connexion"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-fr/strings.xml b/packages/Tethering/res/values-fr/strings.xml
deleted file mode 100644
index 22259c52ab9e..000000000000
--- a/packages/Tethering/res/values-fr/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès activé"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Appuyez pour effectuer la configuration."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Pour en savoir plus, contactez votre administrateur"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"État du point d\'accès et du partage de connexion"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-gl/strings.xml b/packages/Tethering/res/values-gl/strings.xml
deleted file mode 100644
index ded82fcd54ad..000000000000
--- a/packages/Tethering/res/values-gl/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida ou zona wifi activada"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"A conexión compartida está desactivada"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta co administrador para obter información"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona wifi e da conexión compartida"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-gu/strings.xml b/packages/Tethering/res/values-gu/strings.xml
deleted file mode 100644
index 7cbbc2de3d91..000000000000
--- a/packages/Tethering/res/values-gu/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ઇન્ટરનેટ શેર કરવાની સુવિધા અથવા હૉટસ્પૉટ સક્રિય છે"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"સેટઅપ કરવા માટે ટૅપ કરો."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરી છે"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"હૉટસ્પૉટ અને ઇન્ટરનેટ શેર કરવાની સુવિધાનું સ્ટેટસ"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-hi/strings.xml b/packages/Tethering/res/values-hi/strings.xml
deleted file mode 100644
index 08af81b826b3..000000000000
--- a/packages/Tethering/res/values-hi/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग या हॉटस्पॉट चालू है"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"सेट अप करने के लिए टैप करें."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद है"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट और टेदरिंग की स्थिति"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-hr/strings.xml b/packages/Tethering/res/values-hr/strings.xml
deleted file mode 100644
index 827c135f205d..000000000000
--- a/packages/Tethering/res/values-hr/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Modemsko povezivanje ili žarišna točka aktivni"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste postavili."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Modemsko je povezivanje onemogućeno"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Obratite se administratoru da biste saznali pojedinosti"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status žarišne točke i modemskog povezivanja"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-hu/strings.xml b/packages/Tethering/res/values-hu/strings.xml
deleted file mode 100644
index eb68d6babf8f..000000000000
--- a/packages/Tethering/res/values-hu/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Megosztás vagy aktív hotspot"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Koppintson a beállításhoz."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Az internetmegosztás le van tiltva"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"A részletekért forduljon rendszergazdájához"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot és internetmegosztás állapota"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-hy/strings.xml b/packages/Tethering/res/values-hy/strings.xml
deleted file mode 100644
index 912941e53835..000000000000
--- a/packages/Tethering/res/values-hy/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Մոդեմի ռեժիմը միացված է"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Հպեք՝ կարգավորելու համար։"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Մոդեմի ռեժիմն անջատված է"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Թեժ կետի և մոդեմի ռեժիմի կարգավիճակը"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-in/strings.xml b/packages/Tethering/res/values-in/strings.xml
deleted file mode 100644
index a4e175a439e9..000000000000
--- a/packages/Tethering/res/values-in/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering atau hotspot aktif"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Ketuk untuk menyiapkan."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering dinonaktifkan"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hubungi admin untuk mengetahui detailnya"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspot &amp; tethering"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-is/strings.xml b/packages/Tethering/res/values-is/strings.xml
deleted file mode 100644
index e9f6670bcd09..000000000000
--- a/packages/Tethering/res/values-is/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Kveikt á tjóðrun eða aðgangsstað"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Ýttu til að setja upp."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Slökkt er á tjóðrun"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Staða heits reits og tjóðrunar"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-it/strings.xml b/packages/Tethering/res/values-it/strings.xml
deleted file mode 100644
index ffb9196f5ee3..000000000000
--- a/packages/Tethering/res/values-it/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Hotspot o tethering attivo"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tocca per impostare."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering disattivato"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contatta il tuo amministratore per avere informazioni dettagliate"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stato hotspot e tethering"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-iw/strings.xml b/packages/Tethering/res/values-iw/strings.xml
deleted file mode 100644
index 7adcb47350c1..000000000000
--- a/packages/Tethering/res/values-iw/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"נקודה לשיתוף אינטרנט או שיתוף אינטרנט בין מכשירים: בסטטוס פעיל"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"יש להקיש כדי להגדיר."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"שיתוף האינטרנט בין מכשירים מושבת"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"לפרטים, יש לפנות למנהל המערכת"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"סטטוס של נקודה לשיתוף אינטרנט ושיתוף אינטרנט בין מכשירים"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ja/strings.xml b/packages/Tethering/res/values-ja/strings.xml
deleted file mode 100644
index f68a73010b36..000000000000
--- a/packages/Tethering/res/values-ja/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"テザリングまたはアクセス ポイントが有効です"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"タップしてセットアップします。"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"テザリングは無効に設定されています"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳しくは、管理者にお問い合わせください"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"アクセス ポイントとテザリングのステータス"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ka/strings.xml b/packages/Tethering/res/values-ka/strings.xml
deleted file mode 100644
index 7c22e82bd370..000000000000
--- a/packages/Tethering/res/values-ka/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"შეეხეთ დასაყენებლად."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ტეტერინგი გათიშულია"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"უსადენო ქსელის და ტეტერინგის სტატუსი"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-kk/strings.xml b/packages/Tethering/res/values-kk/strings.xml
deleted file mode 100644
index 0857d06de243..000000000000
--- a/packages/Tethering/res/values-kk/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Тетеринг немесе хотспот қосулы"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Реттеу үшін түртіңіз."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Тетеринг өшірілді."</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Мәліметтерді әкімшіден алыңыз."</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Хотспот және тетеринг күйі"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-km/strings.xml b/packages/Tethering/res/values-km/strings.xml
deleted file mode 100644
index 536e3d1703b1..000000000000
--- a/packages/Tethering/res/values-km/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ការភ្ជាប់ ឬហតស្ប៉ត​កំពុងដំណើរការ"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"ចុច​ដើម្បី​រៀបចំ។"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ការភ្ជាប់​ត្រូវបានបិទ"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នក ដើម្បីទទួលបានព័ត៌មានលម្អិត"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ស្ថានភាពនៃការភ្ជាប់ និងហតស្ប៉ត"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-kn/strings.xml b/packages/Tethering/res/values-kn/strings.xml
deleted file mode 100644
index 32f54926f4bf..000000000000
--- a/packages/Tethering/res/values-kn/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"ಸೆಟಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಮತ್ತು ಟೆಥರಿಂಗ್‌ ಸ್ಥಿತಿ"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ko/strings.xml b/packages/Tethering/res/values-ko/strings.xml
deleted file mode 100644
index 156b24786db5..000000000000
--- a/packages/Tethering/res/values-ko/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"테더링 또는 핫스팟 사용"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"설정하려면 탭하세요."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"테더링이 사용 중지됨"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"자세한 정보는 관리자에게 문의하세요."</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"핫스팟 및 테더링 상태"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ky/strings.xml b/packages/Tethering/res/values-ky/strings.xml
deleted file mode 100644
index 18ee5fd35718..000000000000
--- a/packages/Tethering/res/values-ky/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Модем режими күйүп турат"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Жөндөө үчүн таптап коюңуз."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Телефонду модем катары колдонууга болбойт"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Байланыш түйүнүнүн жана модем режиминин статусу"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-lo/strings.xml b/packages/Tethering/res/values-lo/strings.xml
deleted file mode 100644
index b12767018c3a..000000000000
--- a/packages/Tethering/res/values-lo/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ເປີດການປ່ອຍສັນຍານ ຫຼື ຮັອດສະປອດແລ້ວ"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"ແຕະເພື່ອຕັ້ງຄ່າ."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ສະຖານະຮັອດສະປອດ ແລະ ການປ່ອຍສັນຍານ"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-lt/strings.xml b/packages/Tethering/res/values-lt/strings.xml
deleted file mode 100644
index 8427baf39f31..000000000000
--- a/packages/Tethering/res/values-lt/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Įrenginys naudojamas kaip modemas arba įjungtas viešosios interneto prieigos taškas"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Palieskite, kad nustatytumėte."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Įrenginio kaip modemo naudojimas išjungtas"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Viešosios interneto prieigos taško ir įrenginio kaip modemo naudojimo būsena"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-lv/strings.xml b/packages/Tethering/res/values-lv/strings.xml
deleted file mode 100644
index aa2d6990e04f..000000000000
--- a/packages/Tethering/res/values-lv/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Piesaiste vai tīklājs ir aktīvs."</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Pieskarieties, lai to iestatītu."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Piesaiste ir atspējota"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Tīklāja un piesaistes statuss"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml
deleted file mode 100644
index 052ca091ac14..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Warmkol het nie internet nie"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Toestelle kan nie aan internet koppel nie"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Skakel warmkol af"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Warmkol is aan"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Bykomende heffings kan geld terwyl jy swerf"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Gaan voort"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml
deleted file mode 100644
index 0518c5a14f22..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"መገናኛ ነጥቡ በይነመረብ የለውም"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"መሣሪያዎች ከበይነመረብ ጋር መገናኘት አይችሉም"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"መገናኛ ነጥብ ያጥፉ"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"የመገናኛ ነጥብ በርቷል"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ቀጥል"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml
deleted file mode 100644
index e6d8423f4633..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"نقطة الاتصال غير متصلة بالإنترنت."</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"لا يمكن للأجهزة الاتصال بالإنترنت."</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"إيقاف نقطة الاتصال"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"نقطة الاتصال مفعّلة"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"متابعة"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml
deleted file mode 100644
index 4c57f21eaeb3..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"হটস্পটৰ কোনো ইণ্টাৰনেট নাই"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ডিভাইচসমূহ ইণ্টাৰনেটৰ সৈতে সংযোগ কৰিব নোৱাৰি"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"হটস্পট অফ কৰক"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"হটস্পট অন হৈ আছে"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"অব্যাহত ৰাখক"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml
deleted file mode 100644
index 2610ab1bec8d..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotun internetə girişi yoxdur"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Cihazlar internetə qoşula bilmir"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot\'u deaktiv edin"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktivdir"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Davam edin"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml
deleted file mode 100644
index 7b032badf09a..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nema pristup internetu"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji ne mogu da se povežu na internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je uključen"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Možda važe dodatni troškovi u romingu"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml
deleted file mode 100644
index 2362a1e6a539..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хот-спот не падключаны да інтэрнэту"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Прылады не могуць падключацца да інтэрнэту"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Выключыць хот-спот"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хот-спот уключаны"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Працягнуць"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml
deleted file mode 100644
index 6ef1b0bbaf62..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точката за достъп няма връзка с интернет"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Устройствата не могат да се свържат с интернет"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Изключване на точката за достъп"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точката за достъп е включена"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Напред"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml
deleted file mode 100644
index 9a3033c94d2a..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"হটস্পটের সাথে ইন্টারনেট কানেক্ট করা নেই"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ডিভাইস ইন্টারনেটের সাথে কানেক্ট করতে পারছে না"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"হটস্পট বন্ধ করুন"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"হটস্পট চালু আছে"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"চালিয়ে যান"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml
deleted file mode 100644
index 57f6d88a4eba..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Pristupna tačka nema internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji se ne mogu povezati na internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi pristupnu tačku"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Pristupna tačka je uključena"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Mogu nastati dodatni troškovi u romingu"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml
deleted file mode 100644
index e3ad666c0bd4..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"El punt d\'accés Wi‑Fi no té accés a Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Els dispositius no es poden connectar a Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactiva el punt d\'accés Wi‑Fi"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"El punt d\'accés Wi‑Fi està activat"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"És possible que s\'apliquin costos addicionals en itinerància"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continua"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml
deleted file mode 100644
index f0992814c128..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nemá připojení k internetu"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Zařízení se nemohou připojit k internetu"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vypnout hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je aktivní"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Při roamingu mohou být účtovány dodatečné poplatky"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Pokračovat"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml
deleted file mode 100644
index 1fb2374487e8..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspottet har intet internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheder kan ikke oprette forbindelse til internettet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Deaktiver hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspottet er aktiveret"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Der opkræves muligvis yderligere gebyrer ved roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsæt"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml
deleted file mode 100644
index 56d1d1df58a8..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot ist nicht mit dem Internet verbunden"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Geräte können nicht mit dem Internet verbunden werden"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot deaktivieren"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktiviert"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Für das Roaming können zusätzliche Gebühren anfallen"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Weiter"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml
deleted file mode 100644
index 674f1f6798ed..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Το σημείο πρόσβασης Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο."</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Δεν είναι η δυνατή η σύνδεση των συσκευών στο διαδίκτυο."</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Απενεργοποίηση σημείου πρόσβασης Wi-Fi"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Σημείο πρόσβασης Wi-Fi ενεργό"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Συνέχεια"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml
deleted file mode 100644
index 3046a3725d13..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml
deleted file mode 100644
index 3046a3725d13..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml
deleted file mode 100644
index 3046a3725d13..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml
deleted file mode 100644
index 3046a3725d13..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml
deleted file mode 100644
index 20c9b94cd5db..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎Hotspot has no internet‎‏‎‎‏‎"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎Devices can’t connect to internet‎‏‎‎‏‎"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎Turn off hotspot‎‏‎‎‏‎"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎Hotspot is on‎‏‎‎‏‎"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎Additional charges may apply while roaming‎‏‎‎‏‎"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎Continue‎‏‎‎‏‎"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml
deleted file mode 100644
index 956547cc6d0c..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"El hotspot no tiene conexión a Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Los dispositivos no pueden conectarse a Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactiva el hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"El hotspot está activado"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Es posible que se apliquen cargos adicionales por roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml
deleted file mode 100644
index 831ec1fb1e2c..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"El punto de acceso no tiene conexión a Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Los dispositivos no se pueden conectar a Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactivar punto de acceso"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Punto de acceso activado"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Puede que se apliquen cargos adicionales en itinerancia"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml
deleted file mode 100644
index ff8dde542261..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Kuumkohal puudub Interneti-ühendus"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Seadmed ei saa Internetiga ühendust luua"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Lülita kuumkoht välja"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Kuumkoht on sees"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Rändluse kasutamisega võivad kaasneda lisatasud"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Jätka"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml
deleted file mode 100644
index c4f70a3eb48b..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Sare publikoak ez du Interneteko konexiorik"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Gailuak ezin dira konektatu Internetera"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desaktibatu sare publikoa"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Sare publikoa aktibatuta dago"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Egin aurrera"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml
deleted file mode 100644
index 79e3ef11d6e5..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"نقطه اتصال به اینترنت دسترسی ندارد"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"دستگاه‌ها به اینترنت متصل نشدند"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"نقطه اتصال را خاموش کنید"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"نقطه اتصال روشن است"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ادامه"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml
deleted file mode 100644
index 64921bca9fe5..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotilla ei ole internetyhteyttä"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Laitteet eivät voi yhdistää internetiin"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Laita hotspot pois päältä"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot on päällä"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Roaming voi aiheuttaa lisämaksuja"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Jatka"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml
deleted file mode 100644
index eda7b59761cd..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Le point d\'accès n\'est pas connecté à Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Appareils non connectés à Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Désactiver le point d\'accès"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Le point d\'accès est activé"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuer"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml
deleted file mode 100644
index eda7b59761cd..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Le point d\'accès n\'est pas connecté à Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Appareils non connectés à Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Désactiver le point d\'accès"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Le point d\'accès est activé"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuer"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml
deleted file mode 100644
index c163c61fbd8c..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"A zona wifi non ten acceso a Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Os dispositivos non se poden conectar a Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactivar zona wifi"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A zona wifi está activada"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pódense aplicar cargos adicionais en itinerancia"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml
deleted file mode 100644
index 796d42ec5271..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"હૉટસ્પૉટથી ઇન્ટરનેટ ચાલી રહ્યું નથી"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ડિવાઇસ, ઇન્ટરનેટ સાથે કનેક્ટ થઈ શકતા નથી"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"હૉટસ્પૉટ બંધ કરો"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"હૉટસ્પૉટ ચાલુ છે"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"આગળ વધો"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml
deleted file mode 100644
index a2442009b5ab..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"हॉटस्पॉट से इंटरनेट नहीं चल रहा"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"डिवाइस इंटरनेट से कनेक्ट नहीं हो पा रहे"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"हॉटस्पॉट बंद करें"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"हॉटस्पॉट चालू है"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"जारी रखें"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml
deleted file mode 100644
index 41618afb2e89..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Žarišna točka nema pristup internetu"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji se ne mogu povezati s internetom"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi žarišnu točku"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Žarišna je točka uključena"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"U roamingu su mogući dodatni troškovi"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml
deleted file mode 100644
index 39b7a6975b39..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"A hotspot nem csatlakozik az internethez"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Az eszközök nem tudnak csatlakozni az internethez"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot kikapcsolása"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A hotspot be van kapcsolva"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Roaming során további díjak léphetnek fel"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tovább"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml
deleted file mode 100644
index c14ae10ad162..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Թեժ կետը միացված չէ ինտերնետին"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Սարքերը չեն կարողանում միանալ ինտերնետին"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Անջատել թեժ կետը"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Թեժ կետը միացված է"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Շարունակել"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml
deleted file mode 100644
index 1243d22d19c4..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot tidak memiliki koneksi internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Perangkat tidak dapat tersambung ke internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Nonaktifkan hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktif"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Biaya tambahan mungkin berlaku saat roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Lanjutkan"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml
deleted file mode 100644
index 82a7d0123455..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Heitur reitur er ekki nettengdur"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Tæki geta ekki tengst við internetið"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Slökkva á heitum reit"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Kveikt er á heitum reit"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Viðbótargjöld kunna að eiga við í reiki"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Halda áfram"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml
deleted file mode 100644
index a0f52dc89bc7..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"L\'hotspot non ha accesso a Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"I dispositivi non possono connettersi a Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Disattiva l\'hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot attivo"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continua"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml
deleted file mode 100644
index 80807bc23258..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"לנקודה לשיתוף אינטרנט אין חיבור לאינטרנט"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"המכשירים לא יכולים להתחבר לאינטרנט"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"כיבוי הנקודה לשיתוף אינטרנט"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"הנקודה לשיתוף אינטרנט פועלת"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ייתכנו חיובים נוספים בעת נדידה"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"המשך"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml
deleted file mode 100644
index 0e21a7f32292..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"アクセス ポイントがインターネットに接続されていません"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"デバイスをインターネットに接続できません"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"アクセス ポイントを OFF にする"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"アクセス ポイント: ON"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ローミング時に追加料金が発生することがあります"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"続行"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml
deleted file mode 100644
index 6d3b548744ba..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"უსადენო ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"მოწყობილობები ვერ უკავშირდება ინტერნეტს"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"გამორთეთ უსადენო ქსელი"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"უსადენო ქსელი ჩართულია"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"გაგრძელება"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml
deleted file mode 100644
index 985fc3ff9914..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хотспотта интернет жоқ"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Құрылғылар интернетке қосылмайды"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Хотспотты өшіру"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хотспот қосулы"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Жалғастыру"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml
deleted file mode 100644
index 03b5cb6e4b7d..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ហតស្ប៉ត​មិនមាន​អ៊ីនធឺណិត​ទេ"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ឧបករណ៍​មិនអាច​ភ្ជាប់​អ៊ីនធឺណិត​បានទេ"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"បិទ​ហតស្ប៉ត"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ហតស្ប៉ត​ត្រូវបានបើក"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"បន្ត"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml
deleted file mode 100644
index f0adad8e214f..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ಇಂಟರ್ನೆಟ್‌ಗೆ ಸಂಪರ್ಕಗೊಳ್ಳಲು ಸಾಧನಗಳಿಗೆ ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಆಫ್‌ ಮಾಡಿ"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಆನ್ ಆಗಿದೆ"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ಮುಂದುವರಿಸಿ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml
deleted file mode 100644
index 9218e9a09b58..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"핫스팟이 인터넷에 연결되지 않음"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"기기를 인터넷에 연결할 수 없음"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"핫스팟 사용 중지"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"핫스팟 사용 중"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"계속"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml
deleted file mode 100644
index 35a060aa243a..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Байланыш түйүнүндө Интернет жок"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Түзмөктөр Интернетке туташпай жатат"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Туташуу түйүнүн өчүрүү"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Кошулуу түйүнү күйүк"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роумингде кошумча акы алынышы мүмкүн"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Улантуу"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml
deleted file mode 100644
index 1d9203b3690b..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ຮັອດສະປອດບໍ່ມີອິນເຕີເນັດ"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ອິນເຕີເນັດໄດ້"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ປິດຮັອດສະປອດ"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ຮັອດສະປອດເປີດຢູ່"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ສືບຕໍ່"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml
deleted file mode 100644
index db5178bf2d57..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Nėra viešosios interneto prieigos taško interneto ryšio"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Įrenginiams nepavyksta prisijungti prie interneto"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Išjungti viešosios interneto prieigos tašką"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Viešosios interneto prieigos taškas įjungtas"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tęsti"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml
deleted file mode 100644
index c712173ca2b6..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Tīklājam nav interneta savienojuma"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Ierīces nevar izveidot savienojumu ar internetu"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Izslēgt tīklāju"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Tīklājs ir ieslēgts"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tālāk"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml
deleted file mode 100644
index aa4490912b87..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точката на пристап нема интернет"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Уредите не може да се поврзат на интернет"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Исклучи ја точката на пристап"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точката на пристап е вклучена"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"При роаминг може да се наплатат дополнителни трошоци"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продолжи"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml
deleted file mode 100644
index d376fe58704a..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ഹോട്ട്സ്പോട്ടിൽ ഇന്റർനെറ്റ് ലഭ്യമല്ല"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ഉപകരണങ്ങൾ ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്യാനാവില്ല"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ഹോട്ട്‌സ്‌പോട്ട് ഓഫാക്കുക"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ഹോട്ട്സ്പോട്ട് ഓണാണ്"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"തുടരുക"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml
deleted file mode 100644
index 417213f543e9..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Сүлжээний цэг дээр интернэт алга байна"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Төхөөрөмжүүд нь интернэтэд холбогдох боломжгүй байна"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Сүлжээний цэгийг унтраах"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Сүлжээний цэг асаалттай байна"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Үргэлжлүүлэх"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml
deleted file mode 100644
index 2ed153fb17a2..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"हॉटस्पॉटला इंटरनेट नाही"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"डिव्हाइस इंटरनेटला कनेक्ट करू शकत नाहीत"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"हॉटस्पॉट बंद करा"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"हॉटस्पॉट सुरू आहे"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"सुरू ठेवा"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml
deleted file mode 100644
index 50817fd4a24b..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Tempat liputan tiada Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Peranti tidak dapat menyambung kepada Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Matikan tempat liputan"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Tempat liputan dihidupkan"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Caj tambahan mungkin digunakan semasa perayauan"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Teruskan"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml
deleted file mode 100644
index c0d70e3d5f11..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ဟော့စပေါ့တွင် အင်တာနက်မရှိပါ"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"စက်များက အင်တာနက်ချိတ်ဆက်၍ မရပါ"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ဟော့စပေါ့ ပိတ်ရန်"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ရှေ့ဆက်ရန်"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml
deleted file mode 100644
index 1e7f1c6d0a9d..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Wi-Fi-sonen har ikke internettilgang"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheter kan ikke koble til internett"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Slå av Wi-Fi-sonen"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Wi-Fi-sonen er på"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ytterligere kostnader kan påløpe under roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsett"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml
deleted file mode 100644
index 63ce1550345e..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"हटस्पटमा इन्टरनेट छैन"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"यन्त्रहरू इन्टरनेटमा कनेक्ट गर्न सकिएन"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"हटस्पट निष्क्रिय पार्नुहोस्"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"हटस्पट सक्रिय छ"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"जारी राख्नुहोस्"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml
deleted file mode 100644
index bf14a0fceda6..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot heeft geen internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Apparaten kunnen geen verbinding maken met internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot uitschakelen"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is ingeschakeld"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Doorgaan"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml
deleted file mode 100644
index ab87b76cafd5..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ହଟସ୍ପଟରେ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ଡିଭାଇସଗୁଡ଼ିକ ଇଣ୍ଟର୍ନେଟ୍ ସହ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ହଟସ୍ପଟ ବନ୍ଦ କରନ୍ତୁ"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ହଟସ୍ପଟ ଚାଲୁ ଅଛି"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ଜାରି ରଖନ୍ତୁ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml
deleted file mode 100644
index b09f285c2d0e..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ਹੌਟਸਪੌਟ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"ਡੀਵਾਈਸ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਹੋ ਸਕਦੇ"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ਹੌਟਸਪੌਟ ਬੰਦ ਕਰੋ"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ਹੌਟਸਪੌਟ ਚਾਲੂ ਹੈ"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ਜਾਰੀ ਰੱਖੋ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml
deleted file mode 100644
index 8becd0715f6f..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nie ma internetu"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Urządzenia nie mogą połączyć się z internetem"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Wyłącz hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot jest włączony"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Dalej"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml
deleted file mode 100644
index 8e01736f643f..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"O ponto de acesso não tem conexão com a Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não foi possível conectar os dispositivos à Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar ponto de acesso"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"O ponto de acesso está ativado"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pode haver cobranças extras durante o roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml
deleted file mode 100644
index 2356379e2f8f..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"A zona Wi-Fi não tem Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não é possível ligar os dispositivos à Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar zona Wi-Fi"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A zona Wi-Fi está ativada"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Podem aplicar-se custos adicionais em roaming."</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml
deleted file mode 100644
index 8e01736f643f..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"O ponto de acesso não tem conexão com a Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não foi possível conectar os dispositivos à Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar ponto de acesso"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"O ponto de acesso está ativado"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pode haver cobranças extras durante o roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml
deleted file mode 100644
index 2e62bd611cff..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotul nu are internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Dispozitivele nu se pot conecta la internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Dezactivați hotspotul"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspotul este activ"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Se pot aplica taxe suplimentare pentru roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuați"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml
deleted file mode 100644
index a2b1640cb22f..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точка доступа не подключена к Интернету"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Устройства не могут подключаться к Интернету"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Отключить точку доступа"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точка доступа включена"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продолжить"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml
deleted file mode 100644
index 632748a3e8f5..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"හොට්ස්පොට් හට අන්තර්ජාලය නැත"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"උපාංගවලට අන්තර්ජාලයට සම්බන්ධ විය නොහැකිය"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"හොට්ස්පොට් ක්‍රියාවිරහිත කරන්න"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"හොට්ස්පොට් ක්‍රියාත්මකයි"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ඉදිරියට යන්න"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml
deleted file mode 100644
index 247fc1b0e738..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nemá internetové pripojenie"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Zariadenia sa nedajú pripojiť k internetu"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vypnúť hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je zapnutý"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Pokračovať"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml
deleted file mode 100644
index ed223721978a..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Dostopna točka nima internetne povezave"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Naprave ne morejo vzpostaviti internetne povezave"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Izklopi dostopno točko"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Dostopna točka je vklopljena"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Med gostovanjem lahko nastanejo dodatni stroški"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Naprej"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml
deleted file mode 100644
index 4bfab6e47449..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Zona e qasjes për internet nuk ka internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Pajisjet nuk mund të lidhen me internetin"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Çaktivizo zonën e qasjes për internet"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Zona e qasjes për internet është aktive"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Mund të zbatohen tarifime shtesë kur je në roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Vazhdo"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml
deleted file mode 100644
index 478d53a25587..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хотспот нема приступ интернету"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Уређаји не могу да се повежу на интернет"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Искључи хотспот"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хотспот је укључен"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Можда важе додатни трошкови у ромингу"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Настави"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml
deleted file mode 100644
index a793ed648347..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Surfzonen har ingen internetanslutning"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheterna har ingen internetanslutning"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Inaktivera surfzon"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Surfzonen är aktiverad"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ytterligare avgifter kan tillkomma vid roaming"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsätt"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml
deleted file mode 100644
index 18ee457d03cc..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Mtandao pepe hauna intaneti"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Vifaa vimeshindwa kuunganisha kwenye intaneti"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Zima mtandao pepe"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Mtandao pepe umewashwa"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Endelea"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml
deleted file mode 100644
index 7eebd6784ac1..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ஹாட்ஸ்பாட்டில் இணையம் இல்லை"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"சாதனங்களால் இணையத்தில் இணைய இயலவில்லை"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ஹாட்ஸ்பாட்டை ஆஃப் செய்"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ஹாட்ஸ்பாட் ஆன் செய்யப்பட்டுள்ளது"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"தொடர்க"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml
deleted file mode 100644
index 0986534fc7bc..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"హాట్‌స్పాట్‌కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"పరికరాలను ఇంటర్నెట్‌కి కనెక్ట్ చేయడం సాధ్యం కాదు"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"హాట్‌స్పాట్‌ని ఆఫ్ చేయండి"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"హాట్‌స్పాట్ ఆన్‌లో ఉంది"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"కొనసాగించు"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml
deleted file mode 100644
index 3837002b29a8..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ฮอตสปอตไม่ได้เชื่อมต่ออินเทอร์เน็ต"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"อุปกรณ์เชื่อมต่ออินเทอร์เน็ตไม่ได้"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ปิดฮอตสปอต"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ฮอตสปอตเปิดอยู่"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ต่อไป"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml
deleted file mode 100644
index 208f893447de..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Walang internet ang hotspot"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Hindi makakonekta sa internet ang mga device"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"I-off ang hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Naka-on ang hotspot"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Ituloy"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml
deleted file mode 100644
index 3482fafa2d9d..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot\'un internet bağlantısı yok"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Cihazlar internete bağlanamıyor"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot\'u kapat"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot açık"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Dolaşım sırasında ek ücretler uygulanabilir"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Devam"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml
deleted file mode 100644
index dea311443fda..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точка доступу не підключена до Інтернету"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Не вдається підключити пристрої до Інтернету"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Вимкнути точку доступу"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точку доступу ввімкнено"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"У роумінгу може стягуватися додаткова плата"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продовжити"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml
deleted file mode 100644
index 09bc0c9eabb9..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"ہاٹ اسپاٹ میں انٹرنیٹ نہیں ہے"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"آلات انٹرنیٹ سے منسلک نہیں ہو سکتے"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ہاٹ اسپاٹ آف کریں"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ہاٹ اسپاٹ آن ہے"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"جاری رکھیں"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml
deleted file mode 100644
index 715d34808b48..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot internetga ulanmagan"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Qurilmalar internetga ulana olmayapti"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspotni faolsizlantirish"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot yoniq"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Davom etish"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml
deleted file mode 100644
index bf4ee1011b41..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"Điểm phát sóng không có kết nối Internet"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Các thiết bị không thể kết nối Internet"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Tắt điểm phát sóng"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Điểm phát sóng đang bật"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tiếp tục"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml
deleted file mode 100644
index cdb4224bf37b..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"热点没有网络连接"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"设备无法连接到互联网"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"关闭热点"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"热点已开启"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"漫游时可能会产生额外的费用"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"继续"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml
deleted file mode 100644
index 3bb52e491f0a..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"熱點沒有互聯網連線"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"裝置無法連線至互聯網"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"關閉熱點"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"已開啟熱點"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"漫遊時可能需要支付額外費用"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"繼續"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml
deleted file mode 100644
index 298c3eac701a..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"無線基地台沒有網際網路連線"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"裝置無法連上網際網路"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"關閉無線基地台"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"無線基地台已開啟"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"使用漫遊服務可能須支付額外費用"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"繼續"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml
deleted file mode 100644
index 3dc0078834c9..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="6246167638178412020">"I-Hotspot ayina-inthanethi"</string>
- <string name="no_upstream_notification_message" msgid="5010177541603431003">"Amadivayisi awakwazi ukuxhuma ku-inthanethi"</string>
- <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vala i-hotspot"</string>
- <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"I-Hotspot ivuliwe"</string>
- <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string>
- <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Qhubeka"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml
deleted file mode 100644
index 19d659c6ce36..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Verbinding het nie internet nie"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Toestelle kan nie koppel nie"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Skakel verbinding af"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Warmkol of verbinding is aan"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Bykomende heffings kan geld terwyl jy swerf"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml
deleted file mode 100644
index 8995430b4f09..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ማስተሳሰር ምንም በይነመረብ የለውም"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"መሣሪያዎችን ማገናኘት አይቻልም"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ማስተሳሰርን አጥፋ"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml
deleted file mode 100644
index 54f3b5389ae9..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ما مِن اتصال بالإنترنت خلال التوصيل"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"تعذّر اتصال الأجهزة"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"إيقاف التوصيل"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"نقطة الاتصال أو التوصيل مفعّلان"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml
deleted file mode 100644
index e215141c9eb6..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"টে\'ডাৰিং অফ কৰক"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"হটস্পট অথবা টে\'ডাৰিং অন আছে"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml
deleted file mode 100644
index 1fd8e4c963a7..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modemin internetə girişi yoxdur"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Cihazları qoşmaq mümkün deyil"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Modemi deaktiv edin"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot və ya modem aktivdir"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml
deleted file mode 100644
index 1abe4f3aa3c7..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Privezivanje nema pristup internetu"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Povezivanje uređaja nije uspelo"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključi privezivanje"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Uključen je hotspot ili privezivanje"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Možda važe dodatni troškovi u romingu"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml
deleted file mode 100644
index 38dbd1e3914f..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Не ўдалося падключыць прылады"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Выключыць рэжым мадэма"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Хот-спот або рэжым мадэма ўключаны"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml
deleted file mode 100644
index 04b44db5c1a4..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Тетърингът няма връзка с интернет"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Устройствата не могат да установят връзка"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Изключване на тетъринга"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Точката за достъп или тетърингът са включени"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml
deleted file mode 100644
index 579d1be1c1ea..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"ডিভাইস কানেক্ট করতে পারছে না"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"টিথারিং বন্ধ করুন"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"হটস্পট বা টিথারিং চালু আছে"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml
deleted file mode 100644
index 9ce3efe6c39d..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Povezivanje putem mobitela nema internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Uređaji se ne mogu povezati"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključi povezivanje putem mobitela"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Pristupna tačka ili povezivanje putem mobitela je uključeno"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Mogu nastati dodatni troškovi u romingu"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml
deleted file mode 100644
index 46d4c35b9b83..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"La compartició de xarxa no té accés a Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"No es poden connectar els dispositius"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactiva la compartició de xarxa"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"És possible que s\'apliquin costos addicionals en itinerància"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml
deleted file mode 100644
index cc13860b3da1..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nemá připojení k internetu"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Zařízení se nemůžou připojit"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vypnout tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Je zapnutý hotspot nebo tethering"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Při roamingu mohou být účtovány dodatečné poplatky"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml
deleted file mode 100644
index 92c3ae11567d..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Netdeling har ingen internetforbindelse"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enheder kan ikke oprette forbindelse"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Deaktiver netdeling"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot eller netdeling er aktiveret"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Der opkræves muligvis yderligere gebyrer ved roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml
deleted file mode 100644
index 967eb4db2e77..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering hat keinen Internetzugriff"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Geräte können sich nicht verbinden"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering deaktivieren"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot oder Tethering ist aktiviert"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Für das Roaming können zusätzliche Gebühren anfallen"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml
deleted file mode 100644
index 5fb497451f6d..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Δεν είναι δυνατή η σύνδεση των συσκευών"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Απενεργοποιήστε τη σύνδεση"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml
deleted file mode 100644
index 45647f93f246..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml
deleted file mode 100644
index 45647f93f246..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml
deleted file mode 100644
index 45647f93f246..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml
deleted file mode 100644
index 45647f93f246..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml
deleted file mode 100644
index 7877074afc66..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎Tethering has no internet‎‏‎‎‏‎"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎Devices can’t connect‎‏‎‎‏‎"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎Turn off tethering‎‏‎‎‏‎"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎Hotspot or tethering is on‎‏‎‎‏‎"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎Additional charges may apply while roaming‎‏‎‎‏‎"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml
deleted file mode 100644
index 08edd81a6b04..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"La conexión mediante dispositivo móvil no tiene Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"No se pueden conectar los dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión mediante dispositivo móvil"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Se activó el hotspot o la conexión mediante dispositivo móvil"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Es posible que se apliquen cargos adicionales por roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml
deleted file mode 100644
index 79f51d00e2e8..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"La conexión no se puede compartir, porque no hay acceso a Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Los dispositivos no se pueden conectar"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión compartida"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Punto de acceso o conexión compartida activados"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Puede que se apliquen cargos adicionales en itinerancia"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml
deleted file mode 100644
index 2da5f8a6d62a..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Jagamisel puudub internetiühendus"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Seadmed ei saa ühendust luua"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Lülita jagamine välja"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Kuumkoht või jagamine on sisse lülitatud"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rändluse kasutamisega võivad kaasneda lisatasud"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml
deleted file mode 100644
index 2073f2806c18..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Konexioa partekatzeko aukerak ez du Interneteko konexiorik"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Ezin dira konektatu gailuak"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desaktibatu konexioa partekatzeko aukera"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml
deleted file mode 100644
index e21b2a0852c7..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"«اشتراک‌گذاری اینترنت» به اینترنت دسترسی ندارد"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"دستگاه‌ها متصل نمی‌شوند"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"خاموش کردن «اشتراک‌گذاری اینترنت»"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"«نقطه اتصال» یا «اشتراک‌گذاری اینترنت» روشن است"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml
deleted file mode 100644
index 88b0b13eb4b5..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ei jaettavaa internetyhteyttä"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Laitteet eivät voi muodostaa yhteyttä"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Laita yhteyden jakaminen pois päältä"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot tai yhteyden jakaminen on päällä"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Roaming voi aiheuttaa lisämaksuja"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml
deleted file mode 100644
index 3b781bc8db31..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Le partage de connexion n\'est pas connecté à Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossible de connecter les appareils"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Désactiver le partage de connexion"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Le point d\'accès ou le partage de connexion est activé"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml
deleted file mode 100644
index 51d7203c3652..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Aucune connexion à Internet n\'est disponible pour le partage de connexion"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossible de connecter les appareils"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Désactiver le partage de connexion"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Le point d\'accès ou le partage de connexion est activé"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml
deleted file mode 100644
index 008ccb475d66..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"A conexión compartida non ten Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Non se puideron conectar os dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión compartida"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Está activada a zona wifi ou a conexión compartida"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pódense aplicar cargos adicionais en itinerancia"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml
deleted file mode 100644
index f2e3b4df782f..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"ડિવાઇસ કનેક્ટ કરી શકાતા નથી"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml
deleted file mode 100644
index b11839d760c8..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिंग से इंटरनेट नहीं चल रहा"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"डिवाइस कनेक्ट नहीं हो पा रहे"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिंग बंद करें"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हॉटस्पॉट या टेदरिंग चालू है"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml
deleted file mode 100644
index 0a5aca25b1a9..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modemsko povezivanje nema internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Uređaji se ne mogu povezati"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključivanje modemskog povezivanja"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Uključena je žarišna točka ili modemsko povezivanje"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"U roamingu su mogući dodatni troškovi"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml
deleted file mode 100644
index 21c689a44ef8..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nincs internetkapcsolat az internet megosztásához"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Az eszközök nem tudnak csatlakozni"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Internetmegosztás kikapcsolása"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"A hotspot vagy az internetmegosztás be van kapcsolva"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Roaming során további díjak léphetnek fel"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml
deleted file mode 100644
index 689d92870e50..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Մոդեմի ռեժիմի կապը բացակայում է"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Չհաջողվեց միացնել սարքը"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Անջատել մոդեմի ռեժիմը"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Թեժ կետը կամ մոդեմի ռեժիմը միացված է"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml
deleted file mode 100644
index a5f4d19abfe9..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tidak ada koneksi internet di tethering"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Perangkat tidak dapat terhubung"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Nonaktifkan tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot atau tethering aktif"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Biaya tambahan mungkin berlaku saat roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml
deleted file mode 100644
index fc7e8aaf4e42..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tjóðrun er ekki með internettengingu"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Tæki geta ekki tengst"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Slökkva á tjóðrun"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Kveikt er á heitum reit eða tjóðrun"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Viðbótargjöld kunna að eiga við í reiki"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml
deleted file mode 100644
index 6456dd1b806a..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nessuna connessione a Internet per il tethering"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossibile connettere i dispositivi"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Disattiva il tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot o tethering attivi"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml
deleted file mode 100644
index 46b24bd3c508..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"למכשירים אין אפשרות להתחבר"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"השבתה של שיתוף האינטרנט בין מכשירים"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ייתכנו חיובים נוספים בעת נדידה"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml
deleted file mode 100644
index e6eb277b90dd..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"テザリングがインターネットに接続されていません"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"デバイスを接続できません"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"テザリングを OFF にする"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"アクセス ポイントまたはテザリングが ON です"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ローミング時に追加料金が発生することがあります"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml
deleted file mode 100644
index aeddd7101da0..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ტეტერინგს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"მოწყობილობები ვერ ახერხებენ დაკავშირებას"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ტეტერინგის გამორთვა"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ჩართულია უსადენო ქსელი ან ტეტერინგი"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml
deleted file mode 100644
index 255f0a276f95..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Тетеринг режимі интернет байланысынсыз пайдаланылуда"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Құрылғыларды байланыстыру мүмкін емес"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Тетерингіні өшіру"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Хотспот немесе тетеринг қосулы"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml
deleted file mode 100644
index 2bceb1cf7788..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ការភ្ជាប់​មិនមានអ៊ីនធឺណិត​ទេ"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"មិនអាច​ភ្ជាប់ឧបករណ៍​បានទេ"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"បិទការភ្ជាប់"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ហតស្ប៉ត ឬការភ្ជាប់​ត្រូវបានបើក"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml
deleted file mode 100644
index ed769305a679..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ಟೆಥರಿಂಗ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ಟೆಥರಿಂಗ್‌ ಆಫ್ ಮಾಡಿ"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್‌ ಆನ್ ಆಗಿದೆ"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml
deleted file mode 100644
index 6e504941eb8b..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"테더링으로 인터넷을 사용할 수 없음"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"기기에서 연결할 수 없음"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"테더링 사용 중지"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"핫스팟 또는 테더링 켜짐"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml
deleted file mode 100644
index d68128b9a554..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Модем режими Интернети жок колдонулууда"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Түзмөктөр туташпай жатат"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Модем режимин өчүрүү"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Байланыш түйүнү же модем режими күйүк"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роумингде кошумча акы алынышы мүмкүн"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml
deleted file mode 100644
index 03e134a0fc79..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ປິດການປ່ອຍສັນຍານ"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml
deleted file mode 100644
index 652cedc6e6ae..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nėra įrenginio kaip modemo naudojimo interneto ryšio"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Nepavyko susieti įrenginių"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Išjungti įrenginio kaip modemo naudojimą"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml
deleted file mode 100644
index 221972298c18..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Piesaistei nav interneta savienojuma"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Nevar savienot ierīces"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Izslēgt piesaisti"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ir ieslēgts tīklājs vai piesaiste"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml
deleted file mode 100644
index 227f9e346651..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Нема интернет преку мобилен"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Уредите не може да се поврзат"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Исклучи интернет преку мобилен"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Точката на пристап или интернетот преку мобилен е вклучен"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"При роаминг може да се наплатат дополнителни трошоци"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml
deleted file mode 100644
index ec4388512645..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ടെതറിംഗ് ഓഫാക്കുക"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ഹോട്ട്‌സ്‌പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml
deleted file mode 100644
index e263573799ed..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Модемд интернэт алга байна"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Төхөөрөмжүүд холбогдох боломжгүй байна"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Модем болгохыг унтраах"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Сүлжээний цэг эсвэл модем болгох асаалттай байна"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml
deleted file mode 100644
index adf845d078bf..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिंगला इंटरनेट नाही"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"डिव्हाइस कनेक्ट होऊ शकत नाहीत"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिंग बंद करा"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हॉटस्पॉट किंवा टेदरिंग सुरू आहे"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml
deleted file mode 100644
index f65c451e4c21..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Penambatan tiada Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Peranti tidak dapat disambungkan"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Matikan penambatan"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Tempat liputan atau penambatan dihidupkan"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Caj tambahan mungkin digunakan semasa perayauan"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml
deleted file mode 100644
index 4118e775cd84..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"စက်များ ချိတ်ဆက်၍ မရပါ"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml
deleted file mode 100644
index 36853583ce82..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Internettdeling har ikke internettilgang"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enhetene kan ikke koble til"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Slå av internettdeling"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Wi-Fi-sone eller internettdeling er på"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ytterligere kostnader kan påløpe under roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
deleted file mode 100644
index d074f1569933..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"यन्त्रहरू कनेक्ट गर्न सकिएन"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिङ निष्क्रिय पार्नुहोस्"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हटस्पट वा टेदरिङ सक्रिय छ"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml
deleted file mode 100644
index 1d888942f49b..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering heeft geen internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Apparaten kunnen niet worden verbonden"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering uitschakelen"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot of tethering is ingeschakeld"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml
deleted file mode 100644
index 8038815fe804..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml
deleted file mode 100644
index 819833eab07f..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml
deleted file mode 100644
index 65e4380e3916..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nie ma internetu"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Urządzenia nie mogą się połączyć"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Wyłącz tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot lub tethering jest włączony"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml
deleted file mode 100644
index d8866170c191..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"O tethering não tem Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível conectar os dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar o tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ponto de acesso ou tethering ativado"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pode haver cobranças extras durante o roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml
deleted file mode 100644
index bfd45ca0a3e1..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"A ligação (à Internet) via telemóvel não tem Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível ligar os dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar ligação (à Internet) via telemóvel"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Podem aplicar-se custos adicionais em roaming."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml
deleted file mode 100644
index d8866170c191..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"O tethering não tem Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível conectar os dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar o tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ponto de acesso ou tethering ativado"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pode haver cobranças extras durante o roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml
deleted file mode 100644
index 8d87a9e516ad..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Procesul de tethering nu are internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Dispozitivele nu se pot conecta"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Dezactivați procesul de tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"S-a activat hotspotul sau tethering"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Se pot aplica taxe suplimentare pentru roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml
deleted file mode 100644
index dbdb9ebe4931..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Режим модема используется без доступа к Интернету"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Невозможно подключить устройства."</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Отключить режим модема"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Включены точка доступа или режим модема"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml
deleted file mode 100644
index d8301e41c2b2..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ටෙදරින් හට අන්තර්ජාලය නැත"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"උපාංගවලට සම්බන්ධ විය නොහැකිය"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ටෙදරින් ක්‍රියාවිරහිත කරන්න"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"හොට්ස්පොට් හෝ ටෙදරින් ක්‍රියාත්මකයි"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml
deleted file mode 100644
index bef71363f450..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nemá internetové pripojenie"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Zariadenia sa nemôžu pripojiť"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vypnúť tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Je zapnutý hotspot alebo tethering"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml
deleted file mode 100644
index 3202c62e8a3a..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Internetna povezava prek mobilnega telefona ni vzpostavljena"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Napravi se ne moreta povezati"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Izklopi internetno povezavo prek mobilnega telefona"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Med gostovanjem lahko nastanejo dodatni stroški"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml
deleted file mode 100644
index 37f6ad286880..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ndarja e internetit nuk ka internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Pajisjet nuk mund të lidhen"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Çaktivizo ndarjen e internetit"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Zona e qasjes për internet ose ndarja e internetit është aktive"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Mund të zbatohen tarifime shtesë kur je në roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml
deleted file mode 100644
index 5566d03ed13a..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Привезивање нема приступ интернету"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Повезивање уређаја није успело"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Искључи привезивање"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Укључен је хотспот или привезивање"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Можда важе додатни трошкови у ромингу"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml
deleted file mode 100644
index 9765acd0cf46..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Det finns ingen internetanslutning för internetdelningen"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enheterna kan inte anslutas"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Inaktivera internetdelning"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Surfzon eller internetdelning har aktiverats"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ytterligare avgifter kan tillkomma vid roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml
deleted file mode 100644
index cf850c9cd222..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Kipengele cha kusambaza mtandao hakina intaneti"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Imeshindwa kuunganisha vifaa"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Zima kipengele cha kusambaza mtandao"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Umewasha kipengele cha kusambaza mtandao au mtandao pepe"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml
deleted file mode 100644
index f4b15aab19b7..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"சாதனங்களால் இணைய முடியவில்லை"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"இணைப்பு முறையை ஆஃப் செய்"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml
deleted file mode 100644
index 937d34d52027..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"పరికరాలు కనెక్ట్ అవ్వడం లేదు"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"టెథరింగ్‌ను ఆఫ్ చేయండి"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"హాట్‌స్పాట్ లేదా టెథరింగ్ ఆన్‌లో ఉంది"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml
deleted file mode 100644
index f781fae5252e..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"อุปกรณ์เชื่อมต่อไม่ได้"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml
deleted file mode 100644
index 8d5d46537334..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Walang internet ang pag-tether"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Hindi makakonekta ang mga device"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"I-off ang pag-tether"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Naka-on ang Hotspot o pag-tether"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml
deleted file mode 100644
index 80cab33ac05e..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering\'in internet bağlantısı yok"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Cihazlar bağlanamıyor"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering\'i kapat"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot veya tethering açık"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Dolaşım sırasında ek ücretler uygulanabilir"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml
deleted file mode 100644
index c05932a5ae7f..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Телефон, який використовується як модем, не підключений до Інтернету"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Не вдається підключити пристрої"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Вимкнути використання телефона як модема"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Увімкнено точку доступу або використання телефона як модема"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"У роумінгу може стягуватися додаткова плата"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml
deleted file mode 100644
index d820eee8ba3c..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"ٹیدرنگ میں انٹرنیٹ نہیں ہے"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"آلات منسلک نہیں ہو سکتے"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ٹیدرنگ آف کریں"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ہاٹ اسپاٹ یا ٹیدرنگ آن ہے"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml
deleted file mode 100644
index 726148aaee5a..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modem internetga ulanmagan"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Qurilmalar ulanmadi"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Modem rejimini faolsizlantirish"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot yoki modem rejimi yoniq"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml
deleted file mode 100644
index b7cb0456b673..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Không có Internet để chia sẻ kết Internet"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Các thiết bị không thể kết nối"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tắt tính năng chia sẻ Internet"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml
deleted file mode 100644
index af91afff9a4c..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"共享网络未连接到互联网"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"设备无法连接"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"关闭网络共享"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"热点或网络共享已开启"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"漫游时可能会产生额外的费用"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml
deleted file mode 100644
index 28e6b80c01a9..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過網絡共享連線至互聯網"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連接"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉網絡共享"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"熱點或網絡共享已開啟"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"漫遊時可能需要支付額外費用"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml
deleted file mode 100644
index 528a1e52925c..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過網路共用連上網際網路"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連線"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉網路共用"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"無線基地台或網路共用已開啟"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"使用漫遊服務可能須支付額外費用"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml
deleted file mode 100644
index 11eb66621971..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi"</string>
- <string name="no_upstream_notification_message" msgid="3843613362272973447">"Amadivayisi awakwazi ukuxhumeka"</string>
- <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vala ukusebenzisa ifoni njengemodemu"</string>
- <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe"</string>
- <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml
deleted file mode 100644
index 5c5be0466a36..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004/config.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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.
--->
-<resources>
- <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
- "0" for disable this feature. -->
- <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
-
- <!-- Config for showing upstream roaming notification. -->
- <bool name="config_upstream_roaming_notification">true</bool>
-</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc310-mnc004/strings.xml b/packages/Tethering/res/values-mcc310-mnc004/strings.xml
deleted file mode 100644
index ce9ff6080717..000000000000
--- a/packages/Tethering/res/values-mcc310-mnc004/strings.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
--->
-<resources>
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_title">Tethering has no internet</string>
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_message">Devices can\u2019t connect</string>
- <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_disable_button">Turn off tethering</string>
-
- <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
- <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string>
- <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
- <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml
deleted file mode 100644
index 9bfa5317a9e4..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Verbinding het nie internet nie"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Toestelle kan nie koppel nie"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Skakel verbinding af"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Warmkol of verbinding is aan"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Bykomende heffings kan geld terwyl jy swerf"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml
deleted file mode 100644
index 5949dfa776d7..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ማስተሳሰር ምንም በይነመረብ የለውም"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"መሣሪያዎችን ማገናኘት አይቻልም"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ማስተሳሰርን አጥፋ"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml
deleted file mode 100644
index 8467f9b1f5cf..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ما مِن اتصال بالإنترنت خلال التوصيل"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"تعذّر اتصال الأجهزة"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"إيقاف التوصيل"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"نقطة الاتصال أو التوصيل مفعّلان"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml
deleted file mode 100644
index 9776bd89da48..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"টে\'ডাৰিং অফ কৰক"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"হটস্পট অথবা টে\'ডাৰিং অন আছে"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml
deleted file mode 100644
index e6d3eaf9f07c..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Modemin internetə girişi yoxdur"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Cihazları qoşmaq mümkün deyil"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Modemi deaktiv edin"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot və ya modem aktivdir"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml
deleted file mode 100644
index 4c8a1df8eece..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Privezivanje nema pristup internetu"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Povezivanje uređaja nije uspelo"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključi privezivanje"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Uključen je hotspot ili privezivanje"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Možda važe dodatni troškovi u romingu"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml
deleted file mode 100644
index edfa41e1ffd3..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Не ўдалося падключыць прылады"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Выключыць рэжым мадэма"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Хот-спот або рэжым мадэма ўключаны"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml
deleted file mode 100644
index f56398196f51..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Тетърингът няма връзка с интернет"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Устройствата не могат да установят връзка"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Изключване на тетъринга"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Точката за достъп или тетърингът са включени"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml
deleted file mode 100644
index d8ecd2e988f9..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"ডিভাইস কানেক্ট করতে পারছে না"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"টিথারিং বন্ধ করুন"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"হটস্পট বা টিথারিং চালু আছে"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml
deleted file mode 100644
index b85fd5e28577..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Povezivanje putem mobitela nema internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Uređaji se ne mogu povezati"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključi povezivanje putem mobitela"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Pristupna tačka ili povezivanje putem mobitela je uključeno"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Mogu nastati dodatni troškovi u romingu"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml
deleted file mode 100644
index a3572151be6b..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"La compartició de xarxa no té accés a Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"No es poden connectar els dispositius"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactiva la compartició de xarxa"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"És possible que s\'apliquin costos addicionals en itinerància"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml
deleted file mode 100644
index 91196be9e557..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nemá připojení k internetu"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Zařízení se nemůžou připojit"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vypnout tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Je zapnutý hotspot nebo tethering"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Při roamingu mohou být účtovány dodatečné poplatky"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml
deleted file mode 100644
index 196890011d50..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Netdeling har ingen internetforbindelse"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enheder kan ikke oprette forbindelse"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Deaktiver netdeling"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot eller netdeling er aktiveret"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Der opkræves muligvis yderligere gebyrer ved roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml
deleted file mode 100644
index eb3f8c52c0c5..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering hat keinen Internetzugriff"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Geräte können sich nicht verbinden"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering deaktivieren"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot oder Tethering ist aktiviert"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Für das Roaming können zusätzliche Gebühren anfallen"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml
deleted file mode 100644
index 56c3d81b634e..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Δεν είναι δυνατή η σύνδεση των συσκευών"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Απενεργοποιήστε τη σύνδεση"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml
deleted file mode 100644
index dd1a1971cdd8..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml
deleted file mode 100644
index dd1a1971cdd8..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml
deleted file mode 100644
index dd1a1971cdd8..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml
deleted file mode 100644
index dd1a1971cdd8..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml
deleted file mode 100644
index d3347aae207d..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‎‎Tethering has no internet‎‏‎‎‏‎"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‏‏‏‏‎Devices can’t connect‎‏‎‎‏‎"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎Turn off tethering‎‏‎‎‏‎"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎Hotspot or tethering is on‎‏‎‎‏‎"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎Additional charges may apply while roaming‎‏‎‎‏‎"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml
deleted file mode 100644
index 2f0504f07de7..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"La conexión mediante dispositivo móvil no tiene Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"No se pueden conectar los dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión mediante dispositivo móvil"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Se activó el hotspot o la conexión mediante dispositivo móvil"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Es posible que se apliquen cargos adicionales por roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml
deleted file mode 100644
index 2d8f88242502..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"La conexión no se puede compartir, porque no hay acceso a Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Los dispositivos no se pueden conectar"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión compartida"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Punto de acceso o conexión compartida activados"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Puede que se apliquen cargos adicionales en itinerancia"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml
deleted file mode 100644
index 8493c470710d..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Jagamisel puudub internetiühendus"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Seadmed ei saa ühendust luua"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Lülita jagamine välja"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Kuumkoht või jagamine on sisse lülitatud"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rändluse kasutamisega võivad kaasneda lisatasud"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml
deleted file mode 100644
index 33bccab3e88c..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Konexioa partekatzeko aukerak ez du Interneteko konexiorik"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Ezin dira konektatu gailuak"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desaktibatu konexioa partekatzeko aukera"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml
deleted file mode 100644
index cf8a0cc27705..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"«اشتراک‌گذاری اینترنت» به اینترنت دسترسی ندارد"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"دستگاه‌ها متصل نمی‌شوند"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"خاموش کردن «اشتراک‌گذاری اینترنت»"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"«نقطه اتصال» یا «اشتراک‌گذاری اینترنت» روشن است"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml
deleted file mode 100644
index 6a3ab806db98..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Ei jaettavaa internetyhteyttä"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Laitteet eivät voi muodostaa yhteyttä"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Laita yhteyden jakaminen pois päältä"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot tai yhteyden jakaminen on päällä"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Roaming voi aiheuttaa lisämaksuja"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml
deleted file mode 100644
index ffb9bf60472e..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Le partage de connexion n\'est pas connecté à Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossible de connecter les appareils"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Désactiver le partage de connexion"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Le point d\'accès ou le partage de connexion est activé"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml
deleted file mode 100644
index 768bce3f0ab1..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Aucune connexion à Internet n\'est disponible pour le partage de connexion"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossible de connecter les appareils"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Désactiver le partage de connexion"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Le point d\'accès ou le partage de connexion est activé"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml
deleted file mode 100644
index 0c4195a7caf3..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"A conexión compartida non ten Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Non se puideron conectar os dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión compartida"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Está activada a zona wifi ou a conexión compartida"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pódense aplicar cargos adicionais en itinerancia"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml
deleted file mode 100644
index e9d33a7db259..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"ડિવાઇસ કનેક્ટ કરી શકાતા નથી"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml
deleted file mode 100644
index aa418ac5d3bb..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिंग से इंटरनेट नहीं चल रहा"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"डिवाइस कनेक्ट नहीं हो पा रहे"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिंग बंद करें"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हॉटस्पॉट या टेदरिंग चालू है"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml
deleted file mode 100644
index 51c524afbc53..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Modemsko povezivanje nema internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Uređaji se ne mogu povezati"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključivanje modemskog povezivanja"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Uključena je žarišna točka ili modemsko povezivanje"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"U roamingu su mogući dodatni troškovi"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml
deleted file mode 100644
index 164e45edd142..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Nincs internetkapcsolat az internet megosztásához"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Az eszközök nem tudnak csatlakozni"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Internetmegosztás kikapcsolása"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"A hotspot vagy az internetmegosztás be van kapcsolva"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Roaming során további díjak léphetnek fel"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml
deleted file mode 100644
index e76c0a4c80d5..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Մոդեմի ռեժիմի կապը բացակայում է"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Չհաջողվեց միացնել սարքը"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Անջատել մոդեմի ռեժիմը"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Թեժ կետը կամ մոդեմի ռեժիմը միացված է"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml
deleted file mode 100644
index 2b817f8abd17..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tidak ada koneksi internet di tethering"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Perangkat tidak dapat terhubung"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Nonaktifkan tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot atau tethering aktif"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Biaya tambahan mungkin berlaku saat roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml
deleted file mode 100644
index a338d9c7cab8..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tjóðrun er ekki með internettengingu"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Tæki geta ekki tengst"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Slökkva á tjóðrun"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Kveikt er á heitum reit eða tjóðrun"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Viðbótargjöld kunna að eiga við í reiki"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml
deleted file mode 100644
index 77769c2ac56c..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Nessuna connessione a Internet per il tethering"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossibile connettere i dispositivi"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Disattiva il tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot o tethering attivi"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml
deleted file mode 100644
index 5267b5126435..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"למכשירים אין אפשרות להתחבר"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"השבתה של שיתוף האינטרנט בין מכשירים"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ייתכנו חיובים נוספים בעת נדידה"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml
deleted file mode 100644
index 66a9a6dd35c2..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"テザリングがインターネットに接続されていません"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"デバイスを接続できません"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"テザリングを OFF にする"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"アクセス ポイントまたはテザリングが ON です"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ローミング時に追加料金が発生することがあります"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml
deleted file mode 100644
index d8ad8808498f..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ტეტერინგს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"მოწყობილობები ვერ ახერხებენ დაკავშირებას"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ტეტერინგის გამორთვა"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ჩართულია უსადენო ქსელი ან ტეტერინგი"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml
deleted file mode 100644
index 1ddd6b419b57..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Тетеринг режимі интернет байланысынсыз пайдаланылуда"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Құрылғыларды байланыстыру мүмкін емес"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Тетерингіні өшіру"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Хотспот немесе тетеринг қосулы"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml
deleted file mode 100644
index cf5a1379ccc7..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ការភ្ជាប់​មិនមានអ៊ីនធឺណិត​ទេ"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"មិនអាច​ភ្ជាប់ឧបករណ៍​បានទេ"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"បិទការភ្ជាប់"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ហតស្ប៉ត ឬការភ្ជាប់​ត្រូវបានបើក"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml
deleted file mode 100644
index 68ae68bc1998..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ಟೆಥರಿಂಗ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ಟೆಥರಿಂಗ್‌ ಆಫ್ ಮಾಡಿ"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್‌ ಆನ್ ಆಗಿದೆ"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml
deleted file mode 100644
index 17185ba2d063..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"테더링으로 인터넷을 사용할 수 없음"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"기기에서 연결할 수 없음"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"테더링 사용 중지"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"핫스팟 또는 테더링 켜짐"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml
deleted file mode 100644
index 6a9fb9810cc6..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Модем режими Интернети жок колдонулууда"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Түзмөктөр туташпай жатат"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Модем режимин өчүрүү"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Байланыш түйүнү же модем режими күйүк"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роумингде кошумча акы алынышы мүмкүн"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml
deleted file mode 100644
index bcc4b5762678..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ປິດການປ່ອຍສັນຍານ"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml
deleted file mode 100644
index 011c2c11fb88..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Nėra įrenginio kaip modemo naudojimo interneto ryšio"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Nepavyko susieti įrenginių"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Išjungti įrenginio kaip modemo naudojimą"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml
deleted file mode 100644
index 5cb2f3b7aac8..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Piesaistei nav interneta savienojuma"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Nevar savienot ierīces"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Izslēgt piesaisti"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ir ieslēgts tīklājs vai piesaiste"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml
deleted file mode 100644
index 4cbfd887c57e..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Нема интернет преку мобилен"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Уредите не може да се поврзат"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Исклучи интернет преку мобилен"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Точката на пристап или интернетот преку мобилен е вклучен"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"При роаминг може да се наплатат дополнителни трошоци"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml
deleted file mode 100644
index 9cf4eaf34a97..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ടെതറിംഗ് ഓഫാക്കുക"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ഹോട്ട്‌സ്‌പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml
deleted file mode 100644
index 47c82c14d9d6..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Модемд интернэт алга байна"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Төхөөрөмжүүд холбогдох боломжгүй байна"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Модем болгохыг унтраах"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Сүлжээний цэг эсвэл модем болгох асаалттай байна"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml
deleted file mode 100644
index ad9e809ab27d..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिंगला इंटरनेट नाही"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"डिव्हाइस कनेक्ट होऊ शकत नाहीत"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिंग बंद करा"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हॉटस्पॉट किंवा टेदरिंग सुरू आहे"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml
deleted file mode 100644
index e708cb8717b3..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Penambatan tiada Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Peranti tidak dapat disambungkan"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Matikan penambatan"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Tempat liputan atau penambatan dihidupkan"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Caj tambahan mungkin digunakan semasa perayauan"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml
deleted file mode 100644
index ba5462250b05..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"စက်များ ချိတ်ဆက်၍ မရပါ"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml
deleted file mode 100644
index 57db484a2543..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Internettdeling har ikke internettilgang"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enhetene kan ikke koble til"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Slå av internettdeling"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Wi-Fi-sone eller internettdeling er på"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ytterligere kostnader kan påløpe under roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
deleted file mode 100644
index 1503244f5000..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"यन्त्रहरू कनेक्ट गर्न सकिएन"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिङ निष्क्रिय पार्नुहोस्"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हटस्पट वा टेदरिङ सक्रिय छ"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml
deleted file mode 100644
index b08133f4e592..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering heeft geen internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Apparaten kunnen niet worden verbonden"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering uitschakelen"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot of tethering is ingeschakeld"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml
deleted file mode 100644
index 1ad4ca354ad5..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml
deleted file mode 100644
index 88def563d85a..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml
deleted file mode 100644
index f9890abdc26b..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nie ma internetu"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Urządzenia nie mogą się połączyć"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Wyłącz tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot lub tethering jest włączony"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml
deleted file mode 100644
index ce3b88479f09..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"O tethering não tem Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível conectar os dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar o tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ponto de acesso ou tethering ativado"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pode haver cobranças extras durante o roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml
deleted file mode 100644
index 7e883ea57682..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"A ligação (à Internet) via telemóvel não tem Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível ligar os dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar ligação (à Internet) via telemóvel"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Podem aplicar-se custos adicionais em roaming."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml
deleted file mode 100644
index ce3b88479f09..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"O tethering não tem Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível conectar os dispositivos"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar o tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ponto de acesso ou tethering ativado"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pode haver cobranças extras durante o roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml
deleted file mode 100644
index 1009417316ed..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Procesul de tethering nu are internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Dispozitivele nu se pot conecta"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Dezactivați procesul de tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"S-a activat hotspotul sau tethering"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Se pot aplica taxe suplimentare pentru roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml
deleted file mode 100644
index 88683bed95b8..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Режим модема используется без доступа к Интернету"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Невозможно подключить устройства."</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Отключить режим модема"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Включены точка доступа или режим модема"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml
deleted file mode 100644
index 176bcdb797c6..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ටෙදරින් හට අන්තර්ජාලය නැත"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"උපාංගවලට සම්බන්ධ විය නොහැකිය"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ටෙදරින් ක්‍රියාවිරහිත කරන්න"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"හොට්ස්පොට් හෝ ටෙදරින් ක්‍රියාත්මකයි"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml
deleted file mode 100644
index b9e2127fa879..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nemá internetové pripojenie"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Zariadenia sa nemôžu pripojiť"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vypnúť tethering"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Je zapnutý hotspot alebo tethering"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml
deleted file mode 100644
index e8140e686a0c..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Internetna povezava prek mobilnega telefona ni vzpostavljena"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Napravi se ne moreta povezati"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Izklopi internetno povezavo prek mobilnega telefona"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Med gostovanjem lahko nastanejo dodatni stroški"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml
deleted file mode 100644
index 61e698d6e8ab..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Ndarja e internetit nuk ka internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Pajisjet nuk mund të lidhen"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Çaktivizo ndarjen e internetit"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Zona e qasjes për internet ose ndarja e internetit është aktive"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Mund të zbatohen tarifime shtesë kur je në roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml
deleted file mode 100644
index b4c411c35475..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Привезивање нема приступ интернету"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Повезивање уређаја није успело"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Искључи привезивање"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Укључен је хотспот или привезивање"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Можда важе додатни трошкови у ромингу"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml
deleted file mode 100644
index 4f543e47b998..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Det finns ingen internetanslutning för internetdelningen"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enheterna kan inte anslutas"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Inaktivera internetdelning"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Surfzon eller internetdelning har aktiverats"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ytterligare avgifter kan tillkomma vid roaming"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml
deleted file mode 100644
index ac347ab485e0..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Kipengele cha kusambaza mtandao hakina intaneti"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Imeshindwa kuunganisha vifaa"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Zima kipengele cha kusambaza mtandao"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Umewasha kipengele cha kusambaza mtandao au mtandao pepe"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml
deleted file mode 100644
index 2ea2467e5879..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"சாதனங்களால் இணைய முடியவில்லை"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"இணைப்பு முறையை ஆஃப் செய்"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml
deleted file mode 100644
index 9360297dd807..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"పరికరాలు కనెక్ట్ అవ్వడం లేదు"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"టెథరింగ్‌ను ఆఫ్ చేయండి"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"హాట్‌స్పాట్ లేదా టెథరింగ్ ఆన్‌లో ఉంది"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml
deleted file mode 100644
index 9c4d7e08f2b6..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"อุปกรณ์เชื่อมต่อไม่ได้"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml
deleted file mode 100644
index a7c78a593267..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Walang internet ang pag-tether"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Hindi makakonekta ang mga device"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"I-off ang pag-tether"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Naka-on ang Hotspot o pag-tether"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml
deleted file mode 100644
index 93da2c3f7981..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering\'in internet bağlantısı yok"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Cihazlar bağlanamıyor"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering\'i kapat"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot veya tethering açık"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Dolaşım sırasında ek ücretler uygulanabilir"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml
deleted file mode 100644
index ee0dcd2c4b6a..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Телефон, який використовується як модем, не підключений до Інтернету"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Не вдається підключити пристрої"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Вимкнути використання телефона як модема"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Увімкнено точку доступу або використання телефона як модема"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"У роумінгу може стягуватися додаткова плата"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml
deleted file mode 100644
index 41cd28eef9bd..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"ٹیدرنگ میں انٹرنیٹ نہیں ہے"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"آلات منسلک نہیں ہو سکتے"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ٹیدرنگ آف کریں"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ہاٹ اسپاٹ یا ٹیدرنگ آن ہے"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml
deleted file mode 100644
index c847bc943bd4..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Modem internetga ulanmagan"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Qurilmalar ulanmadi"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Modem rejimini faolsizlantirish"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot yoki modem rejimi yoniq"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml
deleted file mode 100644
index a74326f09ec5..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Không có Internet để chia sẻ kết Internet"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Các thiết bị không thể kết nối"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tắt tính năng chia sẻ Internet"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml
deleted file mode 100644
index d7370036e351..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"共享网络未连接到互联网"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"设备无法连接"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"关闭网络共享"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"热点或网络共享已开启"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"漫游时可能会产生额外的费用"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml
deleted file mode 100644
index f378a9dc2cfb..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過網絡共享連線至互聯網"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連接"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉網絡共享"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"熱點或網絡共享已開啟"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"漫遊時可能需要支付額外費用"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml
deleted file mode 100644
index cd653df1dac6..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過網路共用連上網際網路"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連線"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉網路共用"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"無線基地台或網路共用已開啟"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"使用漫遊服務可能須支付額外費用"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml
deleted file mode 100644
index 32f6df56f154..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="no_upstream_notification_title" msgid="611650570559011140">"Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi"</string>
- <string name="no_upstream_notification_message" msgid="6508394877641864863">"Amadivayisi awakwazi ukuxhumeka"</string>
- <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vala ukusebenzisa ifoni njengemodemu"</string>
- <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe"</string>
- <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml
deleted file mode 100644
index 5c5be0466a36..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480/config.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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.
--->
-<resources>
- <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
- "0" for disable this feature. -->
- <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
-
- <!-- Config for showing upstream roaming notification. -->
- <bool name="config_upstream_roaming_notification">true</bool>
-</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc311-mnc480/strings.xml b/packages/Tethering/res/values-mcc311-mnc480/strings.xml
deleted file mode 100644
index ce9ff6080717..000000000000
--- a/packages/Tethering/res/values-mcc311-mnc480/strings.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
--->
-<resources>
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_title">Tethering has no internet</string>
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_message">Devices can\u2019t connect</string>
- <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_disable_button">Turn off tethering</string>
-
- <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
- <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string>
- <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
- <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
-</resources>
diff --git a/packages/Tethering/res/values-mk/strings.xml b/packages/Tethering/res/values-mk/strings.xml
deleted file mode 100644
index 9ad9b9a58935..000000000000
--- a/packages/Tethering/res/values-mk/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Активно е врзување или точка на пристап"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Допрете за поставување."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Врзувањето е оневозможено"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Контактирајте со администраторот за детали"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус на точката на пристап и врзувањето"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ml/strings.xml b/packages/Tethering/res/values-ml/strings.xml
deleted file mode 100644
index 9db79ce220a4..000000000000
--- a/packages/Tethering/res/values-ml/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"സജ്ജീകരിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"വിശദാംശങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ഹോട്ട്‌സ്പോട്ടിന്റെയും ടെതറിംഗിന്റെയും നില"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-mn/strings.xml b/packages/Tethering/res/values-mn/strings.xml
deleted file mode 100644
index 42d1edbaceb9..000000000000
--- a/packages/Tethering/res/values-mn/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Модем болгох эсвэл сүлжээний цэг идэвхтэй байна"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Тохируулахын тулд товшино уу."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Модем болгохыг идэвхгүй болгосон"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Сүлжээний цэг болон модем болгох төлөв"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-mr/strings.xml b/packages/Tethering/res/values-mr/strings.xml
deleted file mode 100644
index 13995b6b8aa5..000000000000
--- a/packages/Tethering/res/values-mr/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग किंवा हॉटस्पॉट अ‍ॅक्टिव्ह आहे"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"सेट करण्यासाठी टॅप करा."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद केले आहे"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"तपशीलांसाठी तुमच्या ॲडमिनशी संपर्क साधा"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट आणि टेदरिंगची स्थिती"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ms/strings.xml b/packages/Tethering/res/values-ms/strings.xml
deleted file mode 100644
index d6a67f37b1de..000000000000
--- a/packages/Tethering/res/values-ms/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Penambatan atau tempat liputan aktif"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Ketik untuk membuat persediaan."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Penambatan dilumpuhkan"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hubungi pentadbir anda untuk mendapatkan maklumat lanjut"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status tempat liputan &amp; penambatan"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-my/strings.xml b/packages/Tethering/res/values-my/strings.xml
deleted file mode 100644
index 49f6b88a7514..000000000000
--- a/packages/Tethering/res/values-my/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းကို ပိတ်ထားသည်"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"အသေးစိတ်အတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ဟော့စပေါ့နှင့် မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း အခြေအနေ"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-nb/strings.xml b/packages/Tethering/res/values-nb/strings.xml
deleted file mode 100644
index 9594e0a70a69..000000000000
--- a/packages/Tethering/res/values-nb/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Internettdeling eller Wi-Fi-sone er aktiv"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Trykk for å konfigurere."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Internettdeling er slått av"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Ta kontakt med administratoren din for å få mer informasjon"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for Wi-Fi-sone og internettdeling"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ne/strings.xml b/packages/Tethering/res/values-ne/strings.xml
deleted file mode 100644
index 72ae3a80a928..000000000000
--- a/packages/Tethering/res/values-ne/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिङ वा हटस्पट सक्रिय छ"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"सेटअप गर्न ट्याप गर्नुहोस्।"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिङ सुविधा असक्षम पारिएको छ"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हटस्पट तथा टेदरिङको स्थिति"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-nl/strings.xml b/packages/Tethering/res/values-nl/strings.xml
deleted file mode 100644
index 18b2bbfc7670..000000000000
--- a/packages/Tethering/res/values-nl/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering of hotspot actief"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tik om in te stellen."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is uitgeschakeld"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Neem contact op met je beheerder voor meer informatie"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status van hotspot en tethering"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-or/strings.xml b/packages/Tethering/res/values-or/strings.xml
deleted file mode 100644
index a15a6db42af6..000000000000
--- a/packages/Tethering/res/values-or/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ଟିଥେରିଂ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"ସେଟ୍ ଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ଟିଥେରିଂ ଅକ୍ଷମ କରାଯାଇଛି"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"ବିବରଣୀଗୁଡ଼ିକ ପାଇଁ ଆପଣଙ୍କ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ହଟସ୍ପଟ୍ ଓ ଟିଥେରିଂ ସ୍ଥିତି"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-pa/strings.xml b/packages/Tethering/res/values-pa/strings.xml
deleted file mode 100644
index a8235e423e47..000000000000
--- a/packages/Tethering/res/values-pa/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ਟੈਦਰਿੰਗ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ਹੌਟਸਪੌਟ ਅਤੇ ਟੈਦਰਿੰਗ ਦੀ ਸਥਿਤੀ"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-pl/strings.xml b/packages/Tethering/res/values-pl/strings.xml
deleted file mode 100644
index ccb017d43fa8..000000000000
--- a/packages/Tethering/res/values-pl/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Aktywny tethering lub punkt dostępu"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Kliknij, by skonfigurować"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering został wyłączony"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot i tethering – stan"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-pt-rBR/strings.xml b/packages/Tethering/res/values-pt-rBR/strings.xml
deleted file mode 100644
index a0a4745f9394..000000000000
--- a/packages/Tethering/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Ponto de acesso ou tethering ativo"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering desativado"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Fale com seu administrador para saber detalhes"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status de ponto de acesso e tethering"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-pt-rPT/strings.xml b/packages/Tethering/res/values-pt-rPT/strings.xml
deleted file mode 100644
index e3f03fcc6934..000000000000
--- a/packages/Tethering/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Ligação (à Internet) via telemóvel ou zona Wi-Fi ativas"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"A ligação (à Internet) via telemóvel está desativada."</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacte o administrador para obter detalhes."</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona Wi-Fi e da ligação (à Internet) via telemóvel"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-pt/strings.xml b/packages/Tethering/res/values-pt/strings.xml
deleted file mode 100644
index a0a4745f9394..000000000000
--- a/packages/Tethering/res/values-pt/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Ponto de acesso ou tethering ativo"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering desativado"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Fale com seu administrador para saber detalhes"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status de ponto de acesso e tethering"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ro/strings.xml b/packages/Tethering/res/values-ro/strings.xml
deleted file mode 100644
index 5706a4a69c79..000000000000
--- a/packages/Tethering/res/values-ro/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering sau hotspot activ"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Atingeți ca să configurați."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tetheringul este dezactivat"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contactați administratorul pentru detalii"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Starea hotspotului și a tetheringului"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ru/strings.xml b/packages/Tethering/res/values-ru/strings.xml
deleted file mode 100644
index 7cb6f7db3fc8..000000000000
--- a/packages/Tethering/res/values-ru/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Включен режим модема или точка доступа"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Нажмите, чтобы настроить."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Использование телефона в качестве модема запрещено"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Чтобы узнать подробности, обратитесь к администратору."</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус хот-спота и режима модема"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-si/strings.xml b/packages/Tethering/res/values-si/strings.xml
deleted file mode 100644
index ec34c22de750..000000000000
--- a/packages/Tethering/res/values-si/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"පිහිටුවීමට තට්ටු කරන්න."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ටෙදරින් අබල කර ඇත"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"හොට්ස්පොට් &amp; ටෙදරින් තත්ත්වය"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-sk/strings.xml b/packages/Tethering/res/values-sk/strings.xml
deleted file mode 100644
index 43e787c84f87..000000000000
--- a/packages/Tethering/res/values-sk/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering alebo prístupový bod je aktívny"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím prejdete na nastavenie."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je deaktivovaný"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požiadajte svojho správcu"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-sl/strings.xml b/packages/Tethering/res/values-sl/strings.xml
deleted file mode 100644
index 59433626a115..000000000000
--- a/packages/Tethering/res/values-sl/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Povezava z internetom prek mobilnega telefona ali dostopna točka je aktivna"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Dotaknite se, če želite nastaviti."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezava z internetom prek mobilnega telefona je onemogočena"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Za podrobnosti se obrnite na skrbnika"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stanje dostopne točke in povezave z internetom prek mobilnega telefona"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-sq/strings.xml b/packages/Tethering/res/values-sq/strings.xml
deleted file mode 100644
index 21e11558bb0b..000000000000
--- a/packages/Tethering/res/values-sq/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Ndarja e internetit ose zona e qasjes së internetit është aktive"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Trokit për ta konfiguruar."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Ndarja e internetit është çaktivizuar"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakto me administratorin për detaje"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Statusi i zonës së qasjes dhe ndarjes së internetit"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-sr/strings.xml b/packages/Tethering/res/values-sr/strings.xml
deleted file mode 100644
index e2e4dc6361d4..000000000000
--- a/packages/Tethering/res/values-sr/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Привезивање или хотспот је активан"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Додирните да бисте подесили."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Привезивање је онемогућено"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Потражите детаље од администратора"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус хотспота и привезивања"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-sv/strings.xml b/packages/Tethering/res/values-sv/strings.xml
deleted file mode 100644
index 72702c28587d..000000000000
--- a/packages/Tethering/res/values-sv/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Internetdelning eller surfzon har aktiverats"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Tryck om du vill konfigurera."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Internetdelning har inaktiverats"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakta administratören om du vill veta mer"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Trådlös surfzon och internetdelning har inaktiverats"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-sw/strings.xml b/packages/Tethering/res/values-sw/strings.xml
deleted file mode 100644
index 65e4aa8cebb0..000000000000
--- a/packages/Tethering/res/values-sw/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Kusambaza mtandao au mtandaopepe umewashwa"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Gusa ili uweke mipangilio."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Umezima kipengele cha kusambaza mtandao"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Mtandaopepe na hali ya kusambaza mtandao"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ta/strings.xml b/packages/Tethering/res/values-ta/strings.xml
deleted file mode 100644
index 4aba62d4ab46..000000000000
--- a/packages/Tethering/res/values-ta/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"டெதெரிங் அல்லது ஹாட்ஸ்பாட் இயங்குகிறது"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"அமைக்க, தட்டவும்."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"டெதெரிங் முடக்கப்பட்டுள்ளது"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"விவரங்களுக்கு உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ஹாட்ஸ்பாட் &amp; டெதெரிங் நிலை"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-te/strings.xml b/packages/Tethering/res/values-te/strings.xml
deleted file mode 100644
index 1f917913416f..000000000000
--- a/packages/Tethering/res/values-te/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"టెథరింగ్ లేదా హాట్‌స్పాట్ యాక్టివ్‌గా ఉంది"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"సెటప్ చేయడానికి ట్యాప్ చేయండి."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"టెథరింగ్ డిజేబుల్ చేయబడింది"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"వివరాల కోసం మీ అడ్మిన్‌ని సంప్రదించండి"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"హాట్‌స్పాట్ &amp; టెథరింగ్ స్థితి"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-th/strings.xml b/packages/Tethering/res/values-th/strings.xml
deleted file mode 100644
index 44171c0db82f..000000000000
--- a/packages/Tethering/res/values-th/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือหรือฮอตสปอตทำงานอยู่"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"แตะเพื่อตั้งค่า"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"สถานะฮอตสปอตและการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-tl/strings.xml b/packages/Tethering/res/values-tl/strings.xml
deleted file mode 100644
index 7347dd3e6254..000000000000
--- a/packages/Tethering/res/values-tl/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Aktibo ang pag-tether o hotspot"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"I-tap para i-set up."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Naka-disable ang pag-tether"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status ng hotspot at pag-tether"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-tr/strings.xml b/packages/Tethering/res/values-tr/strings.xml
deleted file mode 100644
index 32030f176574..000000000000
--- a/packages/Tethering/res/values-tr/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering veya hotspot etkin"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamak için dokunun."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering devre dışı bırakıldı"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Ayrıntılı bilgi için yöneticinize başvurun"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot ve tethering durumu"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-uk/strings.xml b/packages/Tethering/res/values-uk/strings.xml
deleted file mode 100644
index 1ca89b3f7813..000000000000
--- a/packages/Tethering/res/values-uk/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Модем чи точка доступу активні"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Натисніть, щоб налаштувати."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Використання телефона як модема вимкнено"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Щоб дізнатися більше, зв\'яжіться з адміністратором"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус точки доступу та модема"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-ur/strings.xml b/packages/Tethering/res/values-ur/strings.xml
deleted file mode 100644
index d72c7d419577..000000000000
--- a/packages/Tethering/res/values-ur/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"ٹیدرنگ غیر فعال ہے"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ہاٹ اسپاٹ اور ٹیتھرنگ کا اسٹیٹس"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-uz/strings.xml b/packages/Tethering/res/values-uz/strings.xml
deleted file mode 100644
index af3b2ebb3500..000000000000
--- a/packages/Tethering/res/values-uz/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Modem rejimi yoki hotspot yoniq"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Sozlash uchun bosing."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Modem rejimi faolsizlantirildi"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Tafsilotlari uchun administratoringizga murojaat qiling"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot va modem rejimi holati"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-vi/strings.xml b/packages/Tethering/res/values-vi/strings.xml
deleted file mode 100644
index 21a0735922c3..000000000000
--- a/packages/Tethering/res/values-vi/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Tính năng chia sẻ Internet hoặc điểm phát sóng đang hoạt động"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Hãy nhấn để thiết lập."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Đã tắt tính năng chia sẻ Internet"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Trạng thái điểm phát sóng và chia sẻ Internet"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-zh-rCN/strings.xml b/packages/Tethering/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 98e3b4b46fdb..000000000000
--- a/packages/Tethering/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"网络共享或热点已启用"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"点按即可设置。"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"网络共享已停用"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"如需了解详情,请与您的管理员联系"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"热点和网络共享状态"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-zh-rHK/strings.xml b/packages/Tethering/res/values-zh-rHK/strings.xml
deleted file mode 100644
index 9cafd42dd43f..000000000000
--- a/packages/Tethering/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"網絡共享或熱點已啟用"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"輕按即可設定。"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"網絡共享已停用"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"請聯絡您的管理員以瞭解詳情"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"熱點和網絡共享狀態"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-zh-rTW/strings.xml b/packages/Tethering/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 50a50bf7a996..000000000000
--- a/packages/Tethering/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"網路共用或無線基地台已啟用"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"輕觸即可進行設定。"</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"網路共用已停用"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳情請洽你的管理員"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"無線基地台與網路共用狀態"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values-zu/strings.xml b/packages/Tethering/res/values-zu/strings.xml
deleted file mode 100644
index f210f8726ee5..000000000000
--- a/packages/Tethering/res/values-zu/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="tethered_notification_title" msgid="6426563586025792944">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string>
- <string name="tethered_notification_message" msgid="64800879503420696">"Thepha ukuze usethe."</string>
- <string name="disable_tether_notification_title" msgid="3004509127903564191">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string>
- <string name="disable_tether_notification_message" msgid="6717523799293901476">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string>
- <string name="notification_channel_tethering_status" msgid="2663463891530932727">"I-Hotspot nesimo sokusebenzisa ifoni njengemodemu"</string>
- <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
- <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
- <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
- <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
- <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
-</resources>
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
deleted file mode 100644
index 5f8d2997197f..000000000000
--- a/packages/Tethering/res/values/config.xml
+++ /dev/null
@@ -1,194 +0,0 @@
-<?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.
--->
-<resources>
- <!--
- OEMs that wish to change the below settings must do so via a runtime resource overlay package
- and *NOT* by changing this file. This file is part of the tethering mainline module.
- TODO: define two resources for each config item: a default_* resource and a config_* resource,
- config_* is empty by default but may be overridden by RROs.
- -->
- <!-- List of regexpressions describing the interface (if any) that represent tetherable
- USB interfaces. If the device doesn't want to support tethering over USB this should
- be empty. An example would be "usb.*" -->
- <string-array translatable="false" name="config_tether_usb_regexs">
- <item>"usb\\d"</item>
- <item>"rndis\\d"</item>
- </string-array>
-
- <!-- List of regexpressions describing the interface (if any) that represent tetherable
- NCM interfaces. If the device doesn't want to support tethering over NCM this should
- be empty. -->
- <string-array translatable="false" name="config_tether_ncm_regexs">
- </string-array>
-
- <!-- List of regexpressions describing the interface (if any) that represent tetherable
- Wifi interfaces. If the device doesn't want to support tethering over Wifi this
- should be empty. An example would be "softap.*" -->
- <string-array translatable="false" name="config_tether_wifi_regexs">
- <item>"wlan\\d"</item>
- <item>"softap\\d"</item>
- </string-array>
-
- <!-- List of regexpressions describing the interface (if any) that represent tetherable
- WiGig interfaces. If the device doesn't want to support tethering over WiGig this
- should be empty. An example would be "wigig\\d" -->
- <string-array translatable="false" name="config_tether_wigig_regexs">
- <item>"wigig\\d"</item>
- </string-array>
-
- <!-- List of regexpressions describing the interface (if any) that represent tetherable
- Wifi P2P interfaces. If the device doesn't want to support tethering over Wifi P2p this
- should be empty. An example would be "p2p-p2p\\d-.*" -->
- <string-array translatable="false" name="config_tether_wifi_p2p_regexs">
- <item>"p2p-p2p\\d-.*"</item>
- <item>"p2p\\d"</item>
- </string-array>
-
- <!-- List of regexpressions describing the interface (if any) that represent tetherable
- bluetooth interfaces. If the device doesn't want to support tethering over bluetooth this
- should be empty. -->
- <string-array translatable="false" name="config_tether_bluetooth_regexs">
- <item>"bt-pan"</item>
- </string-array>
-
- <!-- Use the BPF offload for tethering when the kernel has support. True by default.
- If the device doesn't want to support tether BPF offload, this should be false.
- Note that this setting could be overridden by device config.
- -->
- <bool translatable="false" name="config_tether_enable_bpf_offload">true</bool>
-
- <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. -->
- <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool>
-
- <!-- Use legacy wifi p2p dedicated address instead of randomize address. -->
- <bool translatable="false" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip">false</bool>
-
- <!-- Dhcp range (min, max) to use for tethering purposes -->
- <string-array translatable="false" name="config_tether_dhcp_range">
- </string-array>
-
- <!-- Used to config periodic polls tether offload stats from tethering offload HAL to make the
- data warnings work. 5000(ms) by default. If the device doesn't want to poll tether
- offload stats, this should be -1. Note that this setting could be override by
- runtime resource overlays.
- -->
- <integer translatable="false" name="config_tether_offload_poll_interval">5000</integer>
-
- <!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI,
- WIFI} values allowable for tethering.
-
- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
- [1,7,0] for TYPE_WIFI, TYPE_BLUETOOTH, and TYPE_MOBILE.
-
- This list is also modified by code within the framework, including:
-
- - TYPE_ETHERNET (9) is prepended to this list, and
-
- - the return value of TelephonyManager.isTetheringApnRequired()
- determines how the array is further modified:
-
- * TRUE (DUN REQUIRED).
- TYPE_MOBILE is removed (if present).
- TYPE_MOBILE_HIPRI is removed (if present).
- TYPE_MOBILE_DUN is appended (if not already present).
-
- * FALSE (DUN NOT REQUIRED).
- TYPE_MOBILE_DUN is removed (if present).
- If both of TYPE_MOBILE{,_HIPRI} are not present:
- TYPE_MOBILE is appended.
- TYPE_MOBILE_HIPRI is appended.
-
- For other changes applied to this list, now and in the future, see
- com.android.networkstack.tethering.TetheringConfiguration.
-
- Note also: the order of this is important. The first upstream type
- for which a satisfying network exists is used.
- -->
- <integer-array translatable="false" name="config_tether_upstream_types">
- </integer-array>
-
- <!-- When true, the tethering upstream network follows the current default
- Internet network (except when the current default network is mobile,
- in which case a DUN network will be used if required).
-
- When true, overrides the config_tether_upstream_types setting above.
- -->
- <bool translatable="false" name="config_tether_upstream_automatic">true</bool>
-
-
- <!-- If the mobile hotspot feature requires provisioning, a package name and class name
- can be provided to launch a supported application that provisions the devices.
- EntitlementManager will send an intent to Settings with the specified package name and
- class name in extras to launch provision app.
- TODO: note what extras here.
-
- See EntitlementManager#runUiTetherProvisioning and
- packages/apps/Settings/src/com/android/settings/network/TetherProvisioningActivity.java
- for more details.
-
- For ui-less/periodic recheck support see config_mobile_hotspot_provision_app_no_ui
- -->
- <!-- The first element is the package name and the second element is the class name
- of the provisioning app -->
- <string-array translatable="false" name="config_mobile_hotspot_provision_app">
- <!--
- <item>com.example.provisioning</item>
- <item>com.example.provisioning.Activity</item>
- -->
- </string-array>
-
- <!-- If the mobile hotspot feature requires provisioning, an action can be provided
- that will be broadcast in non-ui cases for checking the provisioning status.
- EntitlementManager will pass the specified name to Settings and Settings would
- launch provisioning app by sending an intent with the package name.
-
- A second broadcast, action defined by config_mobile_hotspot_provision_response,
- will be sent back to notify if provisioning succeeded or not. The response will
- match that of the activity in config_mobile_hotspot_provision_app, but instead
- contained within the int extra "EntitlementResult".
- TODO: provide the system api for "EntitlementResult" extra and note it here.
-
- See EntitlementManager#runSilentTetherProvisioning and
- packages/apps/Settings/src/com/android/settings/wifi/tether/TetherService.java for more
- details.
- -->
- <string translatable="false" name="config_mobile_hotspot_provision_app_no_ui"></string>
-
- <!-- Sent in response to a provisioning check. The caller must hold the
- permission android.permission.TETHER_PRIVILEGED for Settings to
- receive this response.
-
- See config_mobile_hotspot_provision_response
- -->
- <string translatable="false" name="config_mobile_hotspot_provision_response"></string>
-
- <!-- Number of hours between each background provisioning call -->
- <integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer>
-
- <!-- ComponentName of the service used to run no ui tether provisioning. -->
- <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
-
- <!-- No upstream notification is shown when there is a downstream but no upstream that is able
- to do the tethering. -->
- <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
- "-1" for disable this feature. -->
- <integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer>
-
- <!-- Cellular roaming notification is shown when upstream is cellular network and in roaming
- state. -->
- <!-- Config for showing upstream roaming notification. -->
- <bool name="config_upstream_roaming_notification">false</bool>
-</resources>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
deleted file mode 100644
index 0ee7a992ee20..000000000000
--- a/packages/Tethering/res/values/overlayable.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?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.
--->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <overlayable name="TetheringConfig">
- <policy type="product|system|vendor">
- <!-- Params from config.xml that can be overlaid -->
- <item type="array" name="config_tether_usb_regexs"/>
- <item type="array" name="config_tether_ncm_regexs" />
- <item type="array" name="config_tether_wifi_regexs"/>
- <item type="array" name="config_tether_wigig_regexs"/>
- <item type="array" name="config_tether_wifi_p2p_regexs"/>
- <item type="array" name="config_tether_bluetooth_regexs"/>
- <item type="array" name="config_tether_dhcp_range"/>
- <!-- Use the BPF offload for tethering when the kernel has support. True by default.
- If the device doesn't want to support tether BPF offload, this should be false.
- Note that this setting could be overridden by device config.
- -->
- <item type="bool" name="config_tether_enable_bpf_offload"/>
- <item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
- <item type="bool" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip"/>
- <item type="integer" name="config_tether_offload_poll_interval"/>
- <item type="array" name="config_tether_upstream_types"/>
- <item type="bool" name="config_tether_upstream_automatic"/>
- <!-- Configuration values for tethering entitlement check -->
- <item type="array" name="config_mobile_hotspot_provision_app"/>
- <item type="string" name="config_mobile_hotspot_provision_app_no_ui"/>
- <item type="string" name="config_mobile_hotspot_provision_response"/>
- <item type="integer" name="config_mobile_hotspot_provision_check_period"/>
- <item type="string" name="config_wifi_tether_enable"/>
- <!-- Params from config.xml that can be overlaid -->
- </policy>
- </overlayable>
-</resources>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
deleted file mode 100644
index d63c7c5063cc..000000000000
--- a/packages/Tethering/res/values/strings.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?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.
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Shown when the device is tethered -->
- <!-- String for tethered notification title [CHAR LIMIT=200] -->
- <string name="tethered_notification_title">Tethering or hotspot active</string>
- <!-- String for tethered notification message [CHAR LIMIT=200] -->
- <string name="tethered_notification_message">Tap to set up.</string>
-
- <!-- This notification is shown when tethering has been disabled on a user's device.
- The device is managed by the user's employer. Tethering can't be turned on unless the
- IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
- <!-- String for tether disabling notification title [CHAR LIMIT=200] -->
- <string name="disable_tether_notification_title">Tethering is disabled</string>
- <!-- String for tether disabling notification message [CHAR LIMIT=200] -->
- <string name="disable_tether_notification_message">Contact your admin for details</string>
-
- <!-- This string should be consistent with the "Hotspot & tethering" text in the "Network and
- Internet" settings page. That is currently the tether_settings_title_all string. -->
- <!-- String for tether notification channel name [CHAR LIMIT=200] -->
- <string name="notification_channel_tethering_status">Hotspot &amp; tethering status</string>
-
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_title"></string>
- <!-- String for no upstream notification message [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_message"></string>
- <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_disable_button"></string>
-
- <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
- <string name="upstream_roaming_notification_title"></string>
- <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
- <string name="upstream_roaming_notification_message"></string>
-</resources>
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
deleted file mode 100644
index 9fda1257b4c9..000000000000
--- a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-/**
- * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion().
- * @hide
- */
-public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
- /**
- * Get the version of the aidl interface implemented by the callbacks.
- */
- @Override
- public int getInterfaceVersion() {
- return IDhcpServerCallbacks.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return IDhcpServerCallbacks.HASH;
- }
-}
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
deleted file mode 100644
index aaaec17bf922..000000000000
--- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
-
-import android.net.LinkAddress;
-import android.util.ArraySet;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.net.Inet4Address;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building.
- *
- * <p>This utility class does not check for validity of the parameters: invalid parameters are
- * reported by the receiving module when unparceling the parcel.
- *
- * @see DhcpServingParams
- * @hide
- */
-public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel {
- public static final int MTU_UNSET = 0;
-
- /**
- * Set the server address and served prefix for the DHCP server.
- *
- * <p>This parameter is required.
- */
- public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) {
- this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
- this.serverAddrPrefixLength = serverAddr.getPrefixLength();
- return this;
- }
-
- /**
- * Set the default routers to be advertised to DHCP clients.
- *
- * <p>Each router must be inside the served prefix. This may be an empty set, but it must
- * always be set explicitly.
- */
- public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
- this.defaultRouters = toIntArray(defaultRouters);
- return this;
- }
-
- /**
- * Set the default routers to be advertised to DHCP clients.
- *
- * <p>Each router must be inside the served prefix. This may be an empty list of routers,
- * but it must always be set explicitly.
- */
- public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
- return setDefaultRouters(newArraySet(defaultRouters));
- }
-
- /**
- * Convenience method to build the parameters with no default router.
- *
- * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
- */
- public DhcpServingParamsParcelExt setNoDefaultRouter() {
- return setDefaultRouters();
- }
-
- /**
- * Set the DNS servers to be advertised to DHCP clients.
- *
- * <p>This may be an empty set, but it must always be set explicitly.
- */
- public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
- this.dnsServers = toIntArray(dnsServers);
- return this;
- }
-
- /**
- * Set the DNS servers to be advertised to DHCP clients.
- *
- * <p>This may be an empty list of servers, but it must always be set explicitly.
- */
- public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) {
- return setDnsServers(newArraySet(dnsServers));
- }
-
- /**
- * Convenience method to build the parameters with no DNS server.
- *
- * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
- */
- public DhcpServingParamsParcelExt setNoDnsServer() {
- return setDnsServers();
- }
-
- /**
- * Set excluded addresses that the DHCP server is not allowed to assign to clients.
- *
- * <p>This parameter is optional. DNS servers and default routers are always excluded
- * and do not need to be set here.
- */
- public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
- this.excludedAddrs = toIntArray(excludedAddrs);
- return this;
- }
-
- /**
- * Set excluded addresses that the DHCP server is not allowed to assign to clients.
- *
- * <p>This parameter is optional. DNS servers and default routers are always excluded
- * and do not need to be set here.
- */
- public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
- return setExcludedAddrs(newArraySet(excludedAddrs));
- }
-
- /**
- * Set the lease time for leases assigned by the DHCP server.
- *
- * <p>This parameter is required.
- */
- public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
- this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
- return this;
- }
-
- /**
- * Set the link MTU to be advertised to DHCP clients.
- *
- * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter
- * is optional and defaults to {@link #MTU_UNSET}.
- */
- public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) {
- this.linkMtu = linkMtu;
- return this;
- }
-
- /**
- * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option.
- *
- * <p>If not set, the default value is false.
- */
- public DhcpServingParamsParcelExt setMetered(boolean metered) {
- this.metered = metered;
- return this;
- }
-
- /**
- * Set the client address to tell DHCP server only offer this address.
- * The client's prefix length is the same as server's.
- *
- * <p>If not set, the default value is null.
- */
- public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) {
- this.singleClientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr);
- return this;
- }
-
- /**
- * Set whether the DHCP server should request a new prefix from IpServer when receiving
- * DHCPDECLINE message in certain particular link (e.g. there is only one downstream USB
- * tethering client). If it's false, process DHCPDECLINE message as RFC2131#4.3.3 suggests.
- *
- * <p>If not set, the default value is false.
- */
- public DhcpServingParamsParcelExt setChangePrefixOnDecline(boolean changePrefixOnDecline) {
- this.changePrefixOnDecline = changePrefixOnDecline;
- return this;
- }
-
- private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
- int[] res = new int[addrs.size()];
- int i = 0;
- for (Inet4Address addr : addrs) {
- res[i] = inet4AddressToIntHTH(addr);
- i++;
- }
- return res;
- }
-
- private static ArraySet<Inet4Address> newArraySet(Inet4Address... addrs) {
- ArraySet<Inet4Address> addrSet = new ArraySet<>(addrs.length);
- Collections.addAll(addrSet, addrs);
- return addrSet;
- }
-}
diff --git a/packages/Tethering/src/android/net/ip/DadProxy.java b/packages/Tethering/src/android/net/ip/DadProxy.java
deleted file mode 100644
index e2976b78908c..000000000000
--- a/packages/Tethering/src/android/net/ip/DadProxy.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.net.util.InterfaceParams;
-import android.os.Handler;
-
-import androidx.annotation.VisibleForTesting;
-
-/**
- * Basic Duplicate address detection proxy.
- *
- * @hide
- */
-public class DadProxy {
- private static final String TAG = DadProxy.class.getSimpleName();
-
- @VisibleForTesting
- public static NeighborPacketForwarder naForwarder;
- public static NeighborPacketForwarder nsForwarder;
-
- public DadProxy(Handler h, InterfaceParams tetheredIface) {
- naForwarder = new NeighborPacketForwarder(h, tetheredIface,
- NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
- nsForwarder = new NeighborPacketForwarder(h, tetheredIface,
- NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
- }
-
- /** Stop NS/NA Forwarders. */
- public void stop() {
- naForwarder.stop();
- nsForwarder.stop();
- }
-
- /** Set upstream iface on both forwarders. */
- public void setUpstreamIface(InterfaceParams upstreamIface) {
- naForwarder.setUpstreamIface(upstreamIface);
- nsForwarder.setUpstreamIface(upstreamIface);
- }
-}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
deleted file mode 100644
index 52d59fcdc19b..000000000000
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ /dev/null
@@ -1,1422 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
-import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
-import static android.net.util.NetworkConstants.asByte;
-import static android.net.util.PrefixUtils.asIpPrefix;
-import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
-import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
-
-import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
-
-import android.net.INetd;
-import android.net.INetworkStackStatusCallback;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.MacAddress;
-import android.net.RouteInfo;
-import android.net.TetheredClient;
-import android.net.TetheringManager;
-import android.net.TetheringRequestParcel;
-import android.net.dhcp.DhcpLeaseParcelable;
-import android.net.dhcp.DhcpServerCallbacks;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.DhcpServingParamsParcelExt;
-import android.net.dhcp.IDhcpEventCallbacks;
-import android.net.dhcp.IDhcpServer;
-import android.net.ip.IpNeighborMonitor.NeighborEvent;
-import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.shared.NetdUtils;
-import android.net.shared.RouteUtils;
-import android.net.util.InterfaceParams;
-import android.net.util.InterfaceSet;
-import android.net.util.PrefixUtils;
-import android.net.util.SharedLog;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.networkstack.tethering.BpfCoordinator;
-import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.PrivateAddressCoordinator;
-
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.NetworkInterface;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * Provides the interface to IP-layer serving functionality for a given network
- * interface, e.g. for tethering or "local-only hotspot" mode.
- *
- * @hide
- */
-public class IpServer extends StateMachine {
- public static final int STATE_UNAVAILABLE = 0;
- public static final int STATE_AVAILABLE = 1;
- public static final int STATE_TETHERED = 2;
- public static final int STATE_LOCAL_ONLY = 3;
-
- /** Get string name of |state|.*/
- public static String getStateString(int state) {
- switch (state) {
- case STATE_UNAVAILABLE: return "UNAVAILABLE";
- case STATE_AVAILABLE: return "AVAILABLE";
- case STATE_TETHERED: return "TETHERED";
- case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
- }
- return "UNKNOWN: " + state;
- }
-
- private static final byte DOUG_ADAMS = (byte) 42;
-
- // TODO: have PanService use some visible version of this constant
- private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24";
-
- // TODO: have this configurable
- private static final int DHCP_LEASE_TIME_SECS = 3600;
-
- private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString("00:00:00:00:00:00");
-
- private static final String TAG = "IpServer";
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
- private static final Class[] sMessageClasses = {
- IpServer.class
- };
- private static final SparseArray<String> sMagicDecoderRing =
- MessageUtils.findMessageNames(sMessageClasses);
-
- /** IpServer callback. */
- public static class Callback {
- /**
- * Notify that |who| has changed its tethering state.
- *
- * @param who the calling instance of IpServer
- * @param state one of STATE_*
- * @param lastError one of TetheringManager.TETHER_ERROR_*
- */
- public void updateInterfaceState(IpServer who, int state, int lastError) { }
-
- /**
- * Notify that |who| has new LinkProperties.
- *
- * @param who the calling instance of IpServer
- * @param newLp the new LinkProperties to report
- */
- public void updateLinkProperties(IpServer who, LinkProperties newLp) { }
-
- /**
- * Notify that the DHCP leases changed in one of the IpServers.
- */
- public void dhcpLeasesChanged() { }
-
- /**
- * Request Tethering change.
- *
- * @param tetheringType the downstream type of this IpServer.
- * @param enabled enable or disable tethering.
- */
- public void requestEnableTethering(int tetheringType, boolean enabled) { }
- }
-
- /** Capture IpServer dependencies, for injection. */
- public abstract static class Dependencies {
- /**
- * Create a DadProxy instance to be used by IpServer.
- * To support multiple tethered interfaces concurrently DAD Proxy
- * needs to be supported per IpServer instead of per upstream.
- */
- public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) {
- return new DadProxy(handler, ifParams);
- }
-
- /** Create an IpNeighborMonitor to be used by this IpServer */
- public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log,
- IpNeighborMonitor.NeighborEventConsumer consumer) {
- return new IpNeighborMonitor(handler, log, consumer);
- }
-
- /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
- return new RouterAdvertisementDaemon(ifParams);
- }
-
- /** Get |ifName|'s interface information.*/
- public InterfaceParams getInterfaceParams(String ifName) {
- return InterfaceParams.getByName(ifName);
- }
-
- /** Get |ifName|'s interface index. */
- public int getIfindex(String ifName) {
- try {
- return NetworkInterface.getByName(ifName).getIndex();
- } catch (IOException | NullPointerException e) {
- Log.e(TAG, "Can't determine interface index for interface " + ifName);
- return 0;
- }
- }
-
- /** Create a DhcpServer instance to be used by IpServer. */
- public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
- DhcpServerCallbacks cb);
- }
-
- // request from the user that it wants to tether
- public static final int CMD_TETHER_REQUESTED = BASE_IPSERVER + 1;
- // request from the user that it wants to untether
- public static final int CMD_TETHER_UNREQUESTED = BASE_IPSERVER + 2;
- // notification that this interface is down
- public static final int CMD_INTERFACE_DOWN = BASE_IPSERVER + 3;
- // notification from the {@link Tethering.TetherMainSM} that it had trouble enabling IP
- // Forwarding
- public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IPSERVER + 4;
- // notification from the {@link Tethering.TetherMainSM} SM that it had trouble disabling IP
- // Forwarding
- public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IPSERVER + 5;
- // notification from the {@link Tethering.TetherMainSM} SM that it had trouble starting
- // tethering
- public static final int CMD_START_TETHERING_ERROR = BASE_IPSERVER + 6;
- // notification from the {@link Tethering.TetherMainSM} that it had trouble stopping tethering
- public static final int CMD_STOP_TETHERING_ERROR = BASE_IPSERVER + 7;
- // notification from the {@link Tethering.TetherMainSM} that it had trouble setting the DNS
- // forwarders
- public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IPSERVER + 8;
- // the upstream connection has changed
- public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9;
- // new IPv6 tethering parameters need to be processed
- public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
- // new neighbor cache entry on our interface
- public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11;
- // request from DHCP server that it wants to have a new prefix
- public static final int CMD_NEW_PREFIX_REQUEST = BASE_IPSERVER + 12;
- // request from PrivateAddressCoordinator to restart tethering.
- public static final int CMD_NOTIFY_PREFIX_CONFLICT = BASE_IPSERVER + 13;
-
- private final State mInitialState;
- private final State mLocalHotspotState;
- private final State mTetheredState;
- private final State mUnavailableState;
- private final State mWaitingForRestartState;
-
- private final SharedLog mLog;
- private final INetd mNetd;
- @NonNull
- private final BpfCoordinator mBpfCoordinator;
- private final Callback mCallback;
- private final InterfaceController mInterfaceCtrl;
- private final PrivateAddressCoordinator mPrivateAddressCoordinator;
-
- private final String mIfaceName;
- private final int mInterfaceType;
- private final LinkProperties mLinkProperties;
- private final boolean mUsingLegacyDhcp;
- private final boolean mUsingBpfOffload;
-
- private final Dependencies mDeps;
-
- private int mLastError;
- private int mServingMode;
- private InterfaceSet mUpstreamIfaceSet; // may change over time
- private InterfaceParams mInterfaceParams;
- // TODO: De-duplicate this with mLinkProperties above. Currently, these link
- // properties are those selected by the IPv6TetheringCoordinator and relayed
- // to us. By comparison, mLinkProperties contains the addresses and directly
- // connected routes that have been formed from these properties iff. we have
- // succeeded in configuring them and are able to announce them within Router
- // Advertisements (otherwise, we do not add them to mLinkProperties at all).
- private LinkProperties mLastIPv6LinkProperties;
- private RouterAdvertisementDaemon mRaDaemon;
- private DadProxy mDadProxy;
-
- // To be accessed only on the handler thread
- private int mDhcpServerStartIndex = 0;
- private IDhcpServer mDhcpServer;
- private RaParams mLastRaParams;
-
- private LinkAddress mStaticIpv4ServerAddr;
- private LinkAddress mStaticIpv4ClientAddr;
-
- @NonNull
- private List<TetheredClient> mDhcpLeases = Collections.emptyList();
-
- private int mLastIPv6UpstreamIfindex = 0;
-
- private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer {
- public void accept(NeighborEvent e) {
- sendMessage(CMD_NEIGHBOR_EVENT, e);
- }
- }
-
- private final IpNeighborMonitor mIpNeighborMonitor;
-
- private LinkAddress mIpv4Address;
-
- // TODO: Add a dependency object to pass the data members or variables from the tethering
- // object. It helps to reduce the arguments of the constructor.
- public IpServer(
- String ifaceName, Looper looper, int interfaceType, SharedLog log,
- INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,
- boolean usingLegacyDhcp, boolean usingBpfOffload,
- PrivateAddressCoordinator addressCoordinator, Dependencies deps) {
- super(ifaceName, looper);
- mLog = log.forSubComponent(ifaceName);
- mNetd = netd;
- mBpfCoordinator = coordinator;
- mCallback = callback;
- mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
- mIfaceName = ifaceName;
- mInterfaceType = interfaceType;
- mLinkProperties = new LinkProperties();
- mUsingLegacyDhcp = usingLegacyDhcp;
- mUsingBpfOffload = usingBpfOffload;
- mPrivateAddressCoordinator = addressCoordinator;
- mDeps = deps;
- resetLinkProperties();
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
- mServingMode = STATE_AVAILABLE;
-
- mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
- new MyNeighborEventConsumer());
-
- // IP neighbor monitor monitors the neighbor events for adding/removing offload
- // forwarding rules per client. If BPF offload is not supported, don't start listening
- // for neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule,
- // removeIpv6ForwardingRule.
- if (mUsingBpfOffload && !mIpNeighborMonitor.start()) {
- mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
- }
-
- mInitialState = new InitialState();
- mLocalHotspotState = new LocalHotspotState();
- mTetheredState = new TetheredState();
- mUnavailableState = new UnavailableState();
- mWaitingForRestartState = new WaitingForRestartState();
- addState(mInitialState);
- addState(mLocalHotspotState);
- addState(mTetheredState);
- addState(mWaitingForRestartState, mTetheredState);
- addState(mUnavailableState);
-
- setInitialState(mInitialState);
- }
-
- /** Interface name which IpServer served.*/
- public String interfaceName() {
- return mIfaceName;
- }
-
- /**
- * Tethering downstream type. It would be one of TetheringManager#TETHERING_*.
- */
- public int interfaceType() {
- return mInterfaceType;
- }
-
- /** Last error from this IpServer. */
- public int lastError() {
- return mLastError;
- }
-
- /** Serving mode is the current state of IpServer state machine. */
- public int servingMode() {
- return mServingMode;
- }
-
- /** The properties of the network link which IpServer is serving. */
- public LinkProperties linkProperties() {
- return new LinkProperties(mLinkProperties);
- }
-
- /** The address which IpServer is using. */
- public LinkAddress getAddress() {
- return mIpv4Address;
- }
-
- /**
- * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
- * thread.
- */
- public List<TetheredClient> getAllLeases() {
- return Collections.unmodifiableList(mDhcpLeases);
- }
-
- /** Stop this IpServer. After this is called this IpServer should not be used any more. */
- public void stop() {
- sendMessage(CMD_INTERFACE_DOWN);
- }
-
- /**
- * Tethering is canceled. IpServer state machine will be available and wait for
- * next tethering request.
- */
- public void unwanted() {
- sendMessage(CMD_TETHER_UNREQUESTED);
- }
-
- /** Internals. */
-
- private boolean startIPv4() {
- return configureIPv4(true);
- }
-
- /**
- * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
- * handler.
- *
- * <p>Different instances of this class can be created for each call to IDhcpServer methods,
- * with different implementations of the callback, to differentiate handling of success/error in
- * each call.
- */
- private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub {
- @Override
- public void onStatusAvailable(int statusCode) {
- getHandler().post(() -> callback(statusCode));
- }
-
- public abstract void callback(int statusCode);
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return this.HASH;
- }
- }
-
- private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
- private final int mStartIndex;
-
- private DhcpServerCallbacksImpl(int startIndex) {
- mStartIndex = startIndex;
- }
-
- @Override
- public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException {
- getHandler().post(() -> {
- // We are on the handler thread: mDhcpServerStartIndex can be read safely.
- if (mStartIndex != mDhcpServerStartIndex) {
- // This start request is obsolete. Explicitly stop the DHCP server to shut
- // down its thread. When the |server| binder token goes out of scope, the
- // garbage collector will finalize it, which causes the network stack process
- // garbage collector to collect the server itself.
- try {
- server.stop(null);
- } catch (RemoteException e) { }
- return;
- }
-
- if (statusCode != STATUS_SUCCESS) {
- mLog.e("Error obtaining DHCP server: " + statusCode);
- handleError();
- return;
- }
-
- mDhcpServer = server;
- try {
- mDhcpServer.startWithCallbacks(new OnHandlerStatusCallback() {
- @Override
- public void callback(int startStatusCode) {
- if (startStatusCode != STATUS_SUCCESS) {
- mLog.e("Error starting DHCP server: " + startStatusCode);
- handleError();
- }
- }
- }, new DhcpEventCallback());
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- });
- }
-
- private void handleError() {
- mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
- transitionTo(mInitialState);
- }
- }
-
- private class DhcpEventCallback extends IDhcpEventCallbacks.Stub {
- @Override
- public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) {
- final ArrayList<TetheredClient> leases = new ArrayList<>();
- for (DhcpLeaseParcelable lease : leaseParcelables) {
- final LinkAddress address = new LinkAddress(
- intToInet4AddressHTH(lease.netAddr), lease.prefixLength,
- 0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */,
- lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */);
-
- final MacAddress macAddress;
- try {
- macAddress = MacAddress.fromBytes(lease.hwAddr);
- } catch (IllegalArgumentException e) {
- Log.wtf(TAG, "Invalid address received from DhcpServer: "
- + Arrays.toString(lease.hwAddr));
- return;
- }
-
- final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo(
- address, lease.hostname);
- leases.add(new TetheredClient(
- macAddress,
- Collections.singletonList(addressInfo),
- mInterfaceType));
- }
-
- getHandler().post(() -> {
- mDhcpLeases = leases;
- mCallback.dhcpLeasesChanged();
- });
- }
-
- @Override
- public void onNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
- Objects.requireNonNull(currentPrefix);
- sendMessage(CMD_NEW_PREFIX_REQUEST, currentPrefix);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() throws RemoteException {
- return this.HASH;
- }
- }
-
- private RouteInfo getDirectConnectedRoute(@NonNull final LinkAddress ipv4Address) {
- Objects.requireNonNull(ipv4Address);
- return new RouteInfo(PrefixUtils.asIpPrefix(ipv4Address), null, mIfaceName, RTN_UNICAST);
- }
-
- private DhcpServingParamsParcel makeServingParams(@NonNull final Inet4Address defaultRouter,
- @NonNull final Inet4Address dnsServer, @NonNull LinkAddress serverAddr,
- @Nullable Inet4Address clientAddr) {
- final boolean changePrefixOnDecline =
- (mInterfaceType == TetheringManager.TETHERING_NCM && clientAddr == null);
- return new DhcpServingParamsParcelExt()
- .setDefaultRouters(defaultRouter)
- .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
- .setDnsServers(dnsServer)
- .setServerAddr(serverAddr)
- .setMetered(true)
- .setSingleClientAddr(clientAddr)
- .setChangePrefixOnDecline(changePrefixOnDecline);
- // TODO: also advertise link MTU
- }
-
- private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) {
- if (mUsingLegacyDhcp) {
- return true;
- }
-
- final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress();
- final Inet4Address clientAddr = clientLinkAddr == null ? null :
- (Inet4Address) clientLinkAddr.getAddress();
-
- final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */,
- addr /* dnsServer */, serverLinkAddr, clientAddr);
- mDhcpServerStartIndex++;
- mDeps.makeDhcpServer(
- mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
- return true;
- }
-
- private void stopDhcp() {
- // Make all previous start requests obsolete so servers are not started later
- mDhcpServerStartIndex++;
-
- if (mDhcpServer != null) {
- try {
- mDhcpServer.stop(new OnHandlerStatusCallback() {
- @Override
- public void callback(int statusCode) {
- if (statusCode != STATUS_SUCCESS) {
- mLog.e("Error stopping DHCP server: " + statusCode);
- mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
- // Not much more we can do here
- }
- mDhcpLeases.clear();
- getHandler().post(mCallback::dhcpLeasesChanged);
- }
- });
- mDhcpServer = null;
- } catch (RemoteException e) {
- mLog.e("Error stopping DHCP server", e);
- // Not much more we can do here
- }
- }
- }
-
- private boolean configureDhcp(boolean enable, final LinkAddress serverAddr,
- final LinkAddress clientAddr) {
- if (enable) {
- return startDhcp(serverAddr, clientAddr);
- } else {
- stopDhcp();
- return true;
- }
- }
-
- private void stopIPv4() {
- configureIPv4(false);
- // NOTE: All of configureIPv4() will be refactored out of existence
- // into calls to InterfaceController, shared with startIPv4().
- mInterfaceCtrl.clearIPv4Address();
- mPrivateAddressCoordinator.releaseDownstream(this);
- mIpv4Address = null;
- mStaticIpv4ServerAddr = null;
- mStaticIpv4ClientAddr = null;
- }
-
- private boolean configureIPv4(boolean enabled) {
- if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
-
- if (enabled) {
- mIpv4Address = requestIpv4Address(true /* useLastAddress */);
- }
-
- if (mIpv4Address == null) {
- mLog.e("No available ipv4 address");
- return false;
- }
-
- if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
- // BT configures the interface elsewhere: only start DHCP.
- // TODO: make all tethering types behave the same way, and delete the bluetooth
- // code that calls into NetworkManagementService directly.
- return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
- }
-
- final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address);
-
- final Boolean setIfaceUp;
- if (mInterfaceType == TetheringManager.TETHERING_WIFI
- || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P
- || mInterfaceType == TetheringManager.TETHERING_ETHERNET
- || mInterfaceType == TetheringManager.TETHERING_WIGIG) {
- // The WiFi and Ethernet stack has ownership of the interface up/down state.
- // It is unclear whether the Bluetooth or USB stacks will manage their own
- // state.
- setIfaceUp = null;
- } else {
- setIfaceUp = enabled;
- }
- if (!mInterfaceCtrl.setInterfaceConfiguration(mIpv4Address, setIfaceUp)) {
- mLog.e("Error configuring interface");
- if (!enabled) stopDhcp();
- return false;
- }
-
- if (enabled) {
- mLinkProperties.addLinkAddress(mIpv4Address);
- mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address));
- } else {
- mLinkProperties.removeLinkAddress(mIpv4Address);
- mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address));
- }
- return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
- }
-
- private LinkAddress requestIpv4Address(final boolean useLastAddress) {
- if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
-
- if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
- return new LinkAddress(BLUETOOTH_IFACE_ADDR);
- }
-
- return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
- }
-
- private boolean startIPv6() {
- mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
- if (mInterfaceParams == null) {
- mLog.e("Failed to find InterfaceParams");
- stopIPv6();
- return false;
- }
-
- mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
- if (!mRaDaemon.start()) {
- stopIPv6();
- return false;
- }
-
- // TODO: use ShimUtils instead of explicitly checking the version here.
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME)
- || "T".equals(Build.VERSION.CODENAME)) {
- // DAD Proxy starts forwarding packets after IPv6 upstream is present.
- mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams);
- }
-
- return true;
- }
-
- private void stopIPv6() {
- mInterfaceParams = null;
- setRaParams(null);
-
- if (mRaDaemon != null) {
- mRaDaemon.stop();
- mRaDaemon = null;
- }
-
- if (mDadProxy != null) {
- mDadProxy.stop();
- mDadProxy = null;
- }
- }
-
- // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
- // LinkProperties. These have extraneous data filtered out and only the
- // necessary prefixes included (per its prefix distribution policy).
- //
- // TODO: Evaluate using a data structure than is more directly suited to
- // communicating only the relevant information.
- private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) {
- if (mRaDaemon == null) return;
-
- // Avoid unnecessary work on spurious updates.
- if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
- return;
- }
-
- RaParams params = null;
- String upstreamIface = null;
- InterfaceParams upstreamIfaceParams = null;
- int upstreamIfIndex = 0;
-
- if (v6only != null) {
- upstreamIface = v6only.getInterfaceName();
- upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface);
- if (upstreamIfaceParams != null) {
- upstreamIfIndex = upstreamIfaceParams.index;
- }
- params = new RaParams();
- params.mtu = v6only.getMtu();
- params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
-
- if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment);
-
- for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
- if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
-
- final IpPrefix prefix = new IpPrefix(
- linkAddr.getAddress(), linkAddr.getPrefixLength());
- params.prefixes.add(prefix);
-
- final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
- if (dnsServer != null) {
- params.dnses.add(dnsServer);
- }
- }
-
- // Add upstream index to name mapping for the tether stats usage in the coordinator.
- // Although this mapping could be added by both class Tethering and IpServer, adding
- // mapping from IpServer guarantees that the mapping is added before the adding
- // forwarding rules. That is because there are different state machines in both
- // classes. It is hard to guarantee the link property update order between multiple
- // state machines.
- mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface);
- }
-
- // If v6only is null, we pass in null to setRaParams(), which handles
- // deprecation of any existing RA data.
-
- setRaParams(params);
- mLastIPv6LinkProperties = v6only;
-
- updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null);
- mLastIPv6UpstreamIfindex = upstreamIfIndex;
- if (mDadProxy != null) {
- mDadProxy.setUpstreamIface(upstreamIfaceParams);
- }
- }
-
- private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
- final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
- mNetd, toBeRemoved);
- if (removalFailures > 0) {
- mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
- removalFailures));
- }
-
- for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
- }
-
- private void addRoutesToLocalNetwork(@NonNull final List<RouteInfo> toBeAdded) {
- try {
- // It's safe to call networkAddInterface() even if
- // the interface is already in the local_network.
- mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
- try {
- // Add routes from local network. Note that adding routes that
- // already exist does not cause an error (EEXIST is silently ignored).
- RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
- } catch (IllegalStateException e) {
- mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
- return;
- }
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
- return;
- }
-
- for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
- }
-
- private void configureLocalIPv6Routes(
- HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
- // [1] Remove the routes that are deprecated.
- if (!deprecatedPrefixes.isEmpty()) {
- removeRoutesFromLocalNetwork(getLocalRoutesFor(mIfaceName, deprecatedPrefixes));
- }
-
- // [2] Add only the routes that have not previously been added.
- if (newPrefixes != null && !newPrefixes.isEmpty()) {
- HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
- if (mLastRaParams != null) {
- addedPrefixes.removeAll(mLastRaParams.prefixes);
- }
-
- if (!addedPrefixes.isEmpty()) {
- addRoutesToLocalNetwork(getLocalRoutesFor(mIfaceName, addedPrefixes));
- }
- }
- }
-
- private void configureLocalIPv6Dns(
- HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
- // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located?
- if (mNetd == null) {
- if (newDnses != null) newDnses.clear();
- mLog.e("No netd service instance available; not setting local IPv6 addresses");
- return;
- }
-
- // [1] Remove deprecated local DNS IP addresses.
- if (!deprecatedDnses.isEmpty()) {
- for (Inet6Address dns : deprecatedDnses) {
- if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) {
- mLog.e("Failed to remove local dns IP " + dns);
- }
-
- mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
- }
- }
-
- // [2] Add only the local DNS IP addresses that have not previously been added.
- if (newDnses != null && !newDnses.isEmpty()) {
- final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
- if (mLastRaParams != null) {
- addedDnses.removeAll(mLastRaParams.dnses);
- }
-
- for (Inet6Address dns : addedDnses) {
- if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) {
- mLog.e("Failed to add local dns IP " + dns);
- newDnses.remove(dns);
- }
-
- mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
- }
- }
-
- try {
- mNetd.tetherApplyDnsInterfaces();
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to update local DNS caching server");
- if (newDnses != null) newDnses.clear();
- }
- }
-
- private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
- // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
- // offload is disabled. Add this check just in case.
- // TODO: Perhaps remove this protection check.
- if (!mUsingBpfOffload) return;
-
- mBpfCoordinator.tetherOffloadRuleAdd(this, rule);
- }
-
- private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule) {
- // TODO: Perhaps remove this protection check.
- // See the related comment in #addIpv6ForwardingRule.
- if (!mUsingBpfOffload) return;
-
- mBpfCoordinator.tetherOffloadRuleRemove(this, rule);
- }
-
- private void clearIpv6ForwardingRules() {
- if (!mUsingBpfOffload) return;
-
- mBpfCoordinator.tetherOffloadRuleClear(this);
- }
-
- private void updateIpv6ForwardingRule(int newIfindex) {
- // TODO: Perhaps remove this protection check.
- // See the related comment in #addIpv6ForwardingRule.
- if (!mUsingBpfOffload) return;
-
- mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex);
- }
-
- // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
- // changes or if a neighbor event is received.
- private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
- NeighborEvent e) {
- // If we no longer have an upstream, clear forwarding rules and do nothing else.
- if (upstreamIfindex == 0) {
- clearIpv6ForwardingRules();
- return;
- }
-
- // If the upstream interface has changed, remove all rules and re-add them with the new
- // upstream interface.
- if (prevUpstreamIfindex != upstreamIfindex) {
- updateIpv6ForwardingRule(upstreamIfindex);
- }
-
- // If we're here to process a NeighborEvent, do so now.
- // mInterfaceParams must be non-null or the event would not have arrived.
- if (e == null) return;
- if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress()
- || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) {
- return;
- }
-
- // When deleting rules, we still need to pass a non-null MAC, even though it's ignored.
- // Do this here instead of in the Ipv6ForwardingRule constructor to ensure that we never
- // add rules with a null MAC, only delete them.
- MacAddress dstMac = e.isValid() ? e.macAddr : NULL_MAC_ADDRESS;
- Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex,
- mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, dstMac);
- if (e.isValid()) {
- addIpv6ForwardingRule(rule);
- } else {
- removeIpv6ForwardingRule(rule);
- }
- }
-
- private void handleNeighborEvent(NeighborEvent e) {
- if (mInterfaceParams != null
- && mInterfaceParams.index == e.ifindex
- && mInterfaceParams.hasMacAddress) {
- updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e);
- }
- }
-
- private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
- if (!currentPrefix.contains(mIpv4Address.getAddress())
- || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
- Log.e(TAG, "Invalid prefix: " + currentPrefix);
- return;
- }
-
- final LinkAddress deprecatedLinkAddress = mIpv4Address;
- mIpv4Address = requestIpv4Address(false);
- if (mIpv4Address == null) {
- mLog.e("Fail to request a new downstream prefix");
- return;
- }
- final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
-
- // Add new IPv4 address on the interface.
- if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
- mLog.e("Failed to add new IP " + srvAddr);
- return;
- }
-
- // Remove deprecated routes from local network.
- removeRoutesFromLocalNetwork(
- Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
- mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
-
- // Add new routes to local network.
- addRoutesToLocalNetwork(
- Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
- mLinkProperties.addLinkAddress(mIpv4Address);
-
- // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
- // listen on the interface configured with new IPv4 address, that results DNS validation
- // failure of downstream client even if appropriate routes have been configured.
- try {
- mNetd.tetherApplyDnsInterfaces();
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to update local DNS caching server");
- return;
- }
- sendLinkProperties();
-
- // Notify DHCP server that new prefix/route has been applied on IpServer.
- final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
- (Inet4Address) mStaticIpv4ClientAddr.getAddress();
- final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
- srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
- try {
- mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
- @Override
- public void callback(int statusCode) {
- if (statusCode != STATUS_SUCCESS) {
- mLog.e("Error updating DHCP serving params: " + statusCode);
- }
- }
- });
- } catch (RemoteException e) {
- mLog.e("Error updating DHCP serving params", e);
- }
- }
-
- private byte getHopLimit(String upstreamIface, int adjustTTL) {
- try {
- int upstreamHopLimit = Integer.parseUnsignedInt(
- mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit"));
- upstreamHopLimit = upstreamHopLimit + adjustTTL;
- // Cap the hop limit to 255.
- return (byte) Integer.min(upstreamHopLimit, 255);
- } catch (Exception e) {
- mLog.e("Failed to find upstream interface hop limit", e);
- }
- return RaParams.DEFAULT_HOPLIMIT;
- }
-
- private void setRaParams(RaParams newParams) {
- if (mRaDaemon != null) {
- final RaParams deprecatedParams =
- RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
-
- configureLocalIPv6Routes(deprecatedParams.prefixes,
- (newParams != null) ? newParams.prefixes : null);
-
- configureLocalIPv6Dns(deprecatedParams.dnses,
- (newParams != null) ? newParams.dnses : null);
-
- mRaDaemon.buildNewRa(deprecatedParams, newParams);
- }
-
- mLastRaParams = newParams;
- }
-
- private void logMessage(State state, int what) {
- mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
- }
-
- private void sendInterfaceState(int newInterfaceState) {
- mServingMode = newInterfaceState;
- mCallback.updateInterfaceState(this, newInterfaceState, mLastError);
- sendLinkProperties();
- }
-
- private void sendLinkProperties() {
- mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties));
- }
-
- private void resetLinkProperties() {
- mLinkProperties.clear();
- mLinkProperties.setInterfaceName(mIfaceName);
- }
-
- private void maybeConfigureStaticIp(final TetheringRequestParcel request) {
- // Ignore static address configuration if they are invalid or null. In theory, static
- // addresses should not be invalid here because TetheringManager do not allow caller to
- // specify invalid static address configuration.
- if (request == null || request.localIPv4Address == null
- || request.staticClientAddress == null || !checkStaticAddressConfiguration(
- request.localIPv4Address, request.staticClientAddress)) {
- return;
- }
-
- mStaticIpv4ServerAddr = request.localIPv4Address;
- mStaticIpv4ClientAddr = request.staticClientAddress;
- }
-
- class InitialState extends State {
- @Override
- public void enter() {
- sendInterfaceState(STATE_AVAILABLE);
- }
-
- @Override
- public boolean processMessage(Message message) {
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_REQUESTED:
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
- switch (message.arg1) {
- case STATE_LOCAL_ONLY:
- maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
- transitionTo(mLocalHotspotState);
- break;
- case STATE_TETHERED:
- maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
- transitionTo(mTetheredState);
- break;
- default:
- mLog.e("Invalid tethering interface serving state specified.");
- }
- break;
- case CMD_INTERFACE_DOWN:
- transitionTo(mUnavailableState);
- break;
- case CMD_IPV6_TETHER_UPDATE:
- updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class BaseServingState extends State {
- @Override
- public void enter() {
- if (!startIPv4()) {
- mLastError = TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
- return;
- }
-
- try {
- NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));
- } catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
- mLog.e("Error Tethering", e);
- mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
- return;
- }
-
- if (!startIPv6()) {
- mLog.e("Failed to startIPv6");
- // TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
- return;
- }
- }
-
- @Override
- public void exit() {
- // Note that at this point, we're leaving the tethered state. We can fail any
- // of these operations, but it doesn't really change that we have to try them
- // all in sequence.
- stopIPv6();
-
- try {
- NetdUtils.untetherInterface(mNetd, mIfaceName);
- } catch (RemoteException | ServiceSpecificException e) {
- mLastError = TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
- mLog.e("Failed to untether interface: " + e);
- }
-
- stopIPv4();
-
- resetLinkProperties();
- }
-
- @Override
- public boolean processMessage(Message message) {
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_UNREQUESTED:
- transitionTo(mInitialState);
- if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName);
- break;
- case CMD_INTERFACE_DOWN:
- transitionTo(mUnavailableState);
- if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
- break;
- case CMD_IPV6_TETHER_UPDATE:
- updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
- sendLinkProperties();
- break;
- case CMD_IP_FORWARDING_ENABLE_ERROR:
- case CMD_IP_FORWARDING_DISABLE_ERROR:
- case CMD_START_TETHERING_ERROR:
- case CMD_STOP_TETHERING_ERROR:
- case CMD_SET_DNS_FORWARDERS_ERROR:
- mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
- transitionTo(mInitialState);
- break;
- case CMD_NEW_PREFIX_REQUEST:
- handleNewPrefixRequest((IpPrefix) message.obj);
- break;
- case CMD_NOTIFY_PREFIX_CONFLICT:
- mLog.i("restart tethering: " + mInterfaceType);
- mCallback.requestEnableTethering(mInterfaceType, false /* enabled */);
- transitionTo(mWaitingForRestartState);
- break;
- default:
- return false;
- }
- return true;
- }
- }
-
- // Handling errors in BaseServingState.enter() by transitioning is
- // problematic because transitioning during a multi-state jump yields
- // a Log.wtf(). Ultimately, there should be only one ServingState,
- // and forwarding and NAT rules should be handled by a coordinating
- // functional element outside of IpServer.
- class LocalHotspotState extends BaseServingState {
- @Override
- public void enter() {
- super.enter();
- if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {
- transitionTo(mInitialState);
- }
-
- if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
- sendInterfaceState(STATE_LOCAL_ONLY);
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (super.processMessage(message)) return true;
-
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_REQUESTED:
- mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
- break;
- case CMD_TETHER_CONNECTION_CHANGED:
- // Ignored in local hotspot state.
- break;
- default:
- return false;
- }
- return true;
- }
- }
-
- // Handling errors in BaseServingState.enter() by transitioning is
- // problematic because transitioning during a multi-state jump yields
- // a Log.wtf(). Ultimately, there should be only one ServingState,
- // and forwarding and NAT rules should be handled by a coordinating
- // functional element outside of IpServer.
- class TetheredState extends BaseServingState {
- @Override
- public void enter() {
- super.enter();
- if (mLastError != TetheringManager.TETHER_ERROR_NO_ERROR) {
- transitionTo(mInitialState);
- }
-
- if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
- sendInterfaceState(STATE_TETHERED);
- }
-
- @Override
- public void exit() {
- cleanupUpstream();
- super.exit();
- }
-
- private void cleanupUpstream() {
- if (mUpstreamIfaceSet == null) return;
-
- for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
- mUpstreamIfaceSet = null;
- clearIpv6ForwardingRules();
- }
-
- private void cleanupUpstreamInterface(String upstreamIface) {
- // Note that we don't care about errors here.
- // Sometimes interfaces are gone before we get
- // to remove their rules, which generates errors.
- // Just do the best we can.
- try {
- mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());
- }
- try {
- mNetd.tetherRemoveForward(mIfaceName, upstreamIface);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception in disableNat: " + e.toString());
- }
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (super.processMessage(message)) return true;
-
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_REQUESTED:
- mLog.e("CMD_TETHER_REQUESTED while already tethering.");
- break;
- case CMD_TETHER_CONNECTION_CHANGED:
- final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
- if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
- if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
- break;
- }
-
- if (newUpstreamIfaceSet == null) {
- cleanupUpstream();
- break;
- }
-
- for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
- cleanupUpstreamInterface(removed);
- }
-
- final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
- // This makes the call to cleanupUpstream() in the error
- // path for any interface neatly cleanup all the interfaces.
- mUpstreamIfaceSet = newUpstreamIfaceSet;
-
- for (String ifname : added) {
- try {
- mNetd.tetherAddForward(mIfaceName, ifname);
- mNetd.ipfwdAddInterfaceForward(mIfaceName, ifname);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception enabling NAT: " + e.toString());
- cleanupUpstream();
- mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
- transitionTo(mInitialState);
- return true;
- }
- }
- break;
- case CMD_NEIGHBOR_EVENT:
- handleNeighborEvent((NeighborEvent) message.obj);
- break;
- default:
- return false;
- }
- return true;
- }
-
- private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
- if (mUpstreamIfaceSet == null && newIfaces == null) return true;
- if (mUpstreamIfaceSet != null && newIfaces != null) {
- return mUpstreamIfaceSet.equals(newIfaces);
- }
- return false;
- }
-
- private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
- if (mUpstreamIfaceSet == null) return new HashSet<>();
-
- final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
- removed.removeAll(newIfaces.ifnames);
- return removed;
- }
-
- private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
- final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
- if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
- return added;
- }
- }
-
- /**
- * This state is terminal for the per interface state machine. At this
- * point, the tethering main state machine should have removed this interface
- * specific state machine from its list of possible recipients of
- * tethering requests. The state machine itself will hang around until
- * the garbage collector finds it.
- */
- class UnavailableState extends State {
- @Override
- public void enter() {
- mIpNeighborMonitor.stop();
- mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
- sendInterfaceState(STATE_UNAVAILABLE);
- }
- }
-
- class WaitingForRestartState extends State {
- @Override
- public boolean processMessage(Message message) {
- logMessage(this, message.what);
- switch (message.what) {
- case CMD_TETHER_UNREQUESTED:
- transitionTo(mInitialState);
- mLog.i("Untethered (unrequested) and restarting " + mIfaceName);
- mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
- break;
- case CMD_INTERFACE_DOWN:
- transitionTo(mUnavailableState);
- mLog.i("Untethered (interface down) and restarting" + mIfaceName);
- mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
- break;
- default:
- return false;
- }
- return true;
- }
- }
-
- // Accumulate routes representing "prefixes to be assigned to the local
- // interface", for subsequent modification of local_network routing.
- private static ArrayList<RouteInfo> getLocalRoutesFor(
- String ifname, HashSet<IpPrefix> prefixes) {
- final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
- for (IpPrefix ipp : prefixes) {
- localRoutes.add(new RouteInfo(ipp, null, ifname, RTN_UNICAST));
- }
- return localRoutes;
- }
-
- // Given a prefix like 2001:db8::/64 return an address like 2001:db8::1.
- private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
- final byte[] dnsBytes = localPrefix.getRawAddress();
- dnsBytes[dnsBytes.length - 1] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1));
- try {
- return Inet6Address.getByAddress(null, dnsBytes, 0);
- } catch (UnknownHostException e) {
- Log.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
- return null;
- }
- }
-
- private static byte getRandomSanitizedByte(byte dflt, byte... excluded) {
- final byte random = (byte) (new Random()).nextInt();
- for (int value : excluded) {
- if (random == value) return dflt;
- }
- return random;
- }
-}
diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
deleted file mode 100644
index 73fc833fabf5..000000000000
--- a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ETH_P_IPV6;
-import static android.system.OsConstants.IPPROTO_RAW;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-import static android.system.OsConstants.SOCK_RAW;
-
-import android.net.util.InterfaceParams;
-import android.net.util.PacketReader;
-import android.net.util.SocketUtils;
-import android.net.util.TetheringUtils;
-import android.os.Handler;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet6Address;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-
-/**
- * Basic IPv6 Neighbor Advertisement Forwarder.
- *
- * Forward NA packets from upstream iface to tethered iface
- * and NS packets from tethered iface to upstream iface.
- *
- * @hide
- */
-public class NeighborPacketForwarder extends PacketReader {
- private final String mTag;
-
- private FileDescriptor mFd;
-
- // TODO: get these from NetworkStackConstants.
- private static final int IPV6_ADDR_LEN = 16;
- private static final int IPV6_DST_ADDR_OFFSET = 24;
- private static final int IPV6_HEADER_LEN = 40;
- private static final int ETH_HEADER_LEN = 14;
-
- private InterfaceParams mListenIfaceParams, mSendIfaceParams;
-
- private final int mType;
- public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
- public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
-
- public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) {
- super(h);
- mTag = NeighborPacketForwarder.class.getSimpleName() + "-"
- + tetheredInterface.name + "-" + type;
- mType = type;
-
- if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
- mSendIfaceParams = tetheredInterface;
- } else {
- mListenIfaceParams = tetheredInterface;
- }
- }
-
- /** Set new upstream iface and start/stop based on new params. */
- public void setUpstreamIface(InterfaceParams upstreamParams) {
- final InterfaceParams oldUpstreamParams;
-
- if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
- oldUpstreamParams = mListenIfaceParams;
- mListenIfaceParams = upstreamParams;
- } else {
- oldUpstreamParams = mSendIfaceParams;
- mSendIfaceParams = upstreamParams;
- }
-
- if (oldUpstreamParams == null && upstreamParams != null) {
- start();
- } else if (oldUpstreamParams != null && upstreamParams == null) {
- stop();
- } else if (oldUpstreamParams != null && upstreamParams != null
- && oldUpstreamParams.index != upstreamParams.index) {
- stop();
- start();
- }
- }
-
- // TODO: move NetworkStackUtils.closeSocketQuietly to
- // frameworks/libs/net/common/device/com/android/net/module/util/[someclass].
- private void closeSocketQuietly(FileDescriptor fd) {
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ignored) {
- }
- }
-
- @Override
- protected FileDescriptor createFd() {
- try {
- // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used.
- // To keep uniformity in both directions PACKET socket can be used.
- mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
-
- // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type?
- if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
- TetheringUtils.setupNaSocket(mFd);
- } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) {
- TetheringUtils.setupNsSocket(mFd);
- }
-
- SocketAddress bindAddress = SocketUtils.makePacketSocketAddress(
- ETH_P_IPV6, mListenIfaceParams.index);
- Os.bind(mFd, bindAddress);
- } catch (ErrnoException | SocketException e) {
- Log.wtf(mTag, "Failed to create socket", e);
- closeSocketQuietly(mFd);
- return null;
- }
-
- return mFd;
- }
-
- private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) {
- Inet6Address dstAddr;
- try {
- dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf,
- IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN));
- } catch (UnknownHostException | ClassCastException impossible) {
- throw new AssertionError("16-byte array not valid IPv6 address?");
- }
- return dstAddr;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- if (mSendIfaceParams == null) {
- return;
- }
-
- // The BPF filter should already have checked the length of the packet, but...
- if (length < IPV6_HEADER_LEN) {
- return;
- }
- Inet6Address destv6 = getIpv6DestinationAddress(recvbuf);
- if (!destv6.isMulticastAddress()) {
- return;
- }
- InetSocketAddress dest = new InetSocketAddress(destv6, 0);
-
- FileDescriptor fd = null;
- try {
- fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
- SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name);
-
- int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest);
- } catch (ErrnoException | SocketException e) {
- Log.e(mTag, "handlePacket error: " + e);
- } finally {
- closeSocketQuietly(fd);
- }
- }
-}
diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
deleted file mode 100644
index 7c0b7cc7515c..000000000000
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
-import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
-import static android.net.util.TetheringUtils.getAllNodesForScopeId;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.SOCK_RAW;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_SNDTIMEO;
-
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.TrafficStats;
-import android.net.util.InterfaceParams;
-import android.net.util.SocketUtils;
-import android.net.util.TetheringUtils;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructTimeval;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.TrafficStatsConstants;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-
-
-/**
- * Basic IPv6 Router Advertisement Daemon.
- *
- * TODO:
- *
- * - Rewrite using Handler (and friends) so that AlarmManager can deliver
- * "kick" messages when it's time to send a multicast RA.
- *
- * @hide
- */
-public class RouterAdvertisementDaemon {
- private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName();
- private static final byte ICMPV6_ND_ROUTER_SOLICIT = asByte(133);
- private static final byte ICMPV6_ND_ROUTER_ADVERT = asByte(134);
- private static final int MIN_RA_HEADER_SIZE = 16;
-
- // Summary of various timers and lifetimes.
- private static final int MIN_RTR_ADV_INTERVAL_SEC = 300;
- private static final int MAX_RTR_ADV_INTERVAL_SEC = 600;
- // In general, router, prefix, and DNS lifetimes are all advised to be
- // greater than or equal to 3 * MAX_RTR_ADV_INTERVAL. Here, we double
- // that to allow for multicast packet loss.
- //
- // This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent
- // with the https://tools.ietf.org/html/rfc7772#section-4 discussion of
- // "approximately 7 RAs per hour".
- private static final int DEFAULT_LIFETIME = 6 * MAX_RTR_ADV_INTERVAL_SEC;
- // From https://tools.ietf.org/html/rfc4861#section-10 .
- private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3;
- // Both initial and final RAs, but also for changes in RA contents.
- // From https://tools.ietf.org/html/rfc4861#section-10 .
- private static final int MAX_URGENT_RTR_ADVERTISEMENTS = 5;
-
- private static final int DAY_IN_SECONDS = 86_400;
-
- private final InterfaceParams mInterface;
- private final InetSocketAddress mAllNodes;
-
- // This lock is to protect the RA from being updated while being
- // transmitted on another thread (multicast or unicast).
- //
- // TODO: This should be handled with a more RCU-like approach.
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final byte[] mRA = new byte[IPV6_MIN_MTU];
- @GuardedBy("mLock")
- private int mRaLength;
- @GuardedBy("mLock")
- private final DeprecatedInfoTracker mDeprecatedInfoTracker;
- @GuardedBy("mLock")
- private RaParams mRaParams;
-
- private volatile FileDescriptor mSocket;
- private volatile MulticastTransmitter mMulticastTransmitter;
- private volatile UnicastResponder mUnicastResponder;
-
- /** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/
- public static class RaParams {
- // Tethered traffic will have the hop limit properly decremented.
- // Consequently, set the hoplimit greater by one than the upstream
- // unicast hop limit.
- //
- // TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the
- // upstream interface for more correct behaviour.
- static final byte DEFAULT_HOPLIMIT = 65;
-
- public boolean hasDefaultRoute;
- public byte hopLimit;
- public int mtu;
- public HashSet<IpPrefix> prefixes;
- public HashSet<Inet6Address> dnses;
-
- public RaParams() {
- hasDefaultRoute = false;
- hopLimit = DEFAULT_HOPLIMIT;
- mtu = IPV6_MIN_MTU;
- prefixes = new HashSet<IpPrefix>();
- dnses = new HashSet<Inet6Address>();
- }
-
- public RaParams(RaParams other) {
- hasDefaultRoute = other.hasDefaultRoute;
- hopLimit = other.hopLimit;
- mtu = other.mtu;
- prefixes = (HashSet) other.prefixes.clone();
- dnses = (HashSet) other.dnses.clone();
- }
-
- /**
- * Returns the subset of RA parameters that become deprecated when
- * moving from announcing oldRa to announcing newRa.
- *
- * Currently only tracks differences in |prefixes| and |dnses|.
- */
- public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
- RaParams newlyDeprecated = new RaParams();
-
- if (oldRa != null) {
- for (IpPrefix ipp : oldRa.prefixes) {
- if (newRa == null || !newRa.prefixes.contains(ipp)) {
- newlyDeprecated.prefixes.add(ipp);
- }
- }
-
- for (Inet6Address dns : oldRa.dnses) {
- if (newRa == null || !newRa.dnses.contains(dns)) {
- newlyDeprecated.dnses.add(dns);
- }
- }
- }
-
- return newlyDeprecated;
- }
- }
-
- private static class DeprecatedInfoTracker {
- private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
- private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
-
- Set<IpPrefix> getPrefixes() {
- return mPrefixes.keySet();
- }
-
- void putPrefixes(Set<IpPrefix> prefixes) {
- for (IpPrefix ipp : prefixes) {
- mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
- }
- }
-
- void removePrefixes(Set<IpPrefix> prefixes) {
- for (IpPrefix ipp : prefixes) {
- mPrefixes.remove(ipp);
- }
- }
-
- Set<Inet6Address> getDnses() {
- return mDnses.keySet();
- }
-
- void putDnses(Set<Inet6Address> dnses) {
- for (Inet6Address dns : dnses) {
- mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
- }
- }
-
- void removeDnses(Set<Inet6Address> dnses) {
- for (Inet6Address dns : dnses) {
- mDnses.remove(dns);
- }
- }
-
- boolean isEmpty() {
- return mPrefixes.isEmpty() && mDnses.isEmpty();
- }
-
- private boolean decrementCounters() {
- boolean removed = decrementCounter(mPrefixes);
- removed |= decrementCounter(mDnses);
- return removed;
- }
-
- private <T> boolean decrementCounter(HashMap<T, Integer> map) {
- boolean removed = false;
-
- for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
- it.hasNext();) {
- Map.Entry<T, Integer> kv = it.next();
- if (kv.getValue() == 0) {
- it.remove();
- removed = true;
- } else {
- kv.setValue(kv.getValue() - 1);
- }
- }
-
- return removed;
- }
- }
-
- public RouterAdvertisementDaemon(InterfaceParams ifParams) {
- mInterface = ifParams;
- mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
- mDeprecatedInfoTracker = new DeprecatedInfoTracker();
- }
-
- /** Build new RA.*/
- public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
- synchronized (mLock) {
- if (deprecatedParams != null) {
- mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
- mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
- }
-
- if (newParams != null) {
- // Process information that is no longer deprecated.
- mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
- mDeprecatedInfoTracker.removeDnses(newParams.dnses);
- }
-
- mRaParams = newParams;
- assembleRaLocked();
- }
-
- maybeNotifyMulticastTransmitter();
- }
-
- /** Start router advertisement daemon. */
- public boolean start() {
- if (!createSocket()) {
- return false;
- }
-
- mMulticastTransmitter = new MulticastTransmitter();
- mMulticastTransmitter.start();
-
- mUnicastResponder = new UnicastResponder();
- mUnicastResponder.start();
-
- return true;
- }
-
- /** Stop router advertisement daemon. */
- public void stop() {
- closeSocket();
- // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
- // the thread's termination.
- maybeNotifyMulticastTransmitter();
- mMulticastTransmitter = null;
- mUnicastResponder = null;
- }
-
- @GuardedBy("mLock")
- private void assembleRaLocked() {
- final ByteBuffer ra = ByteBuffer.wrap(mRA);
- ra.order(ByteOrder.BIG_ENDIAN);
-
- final boolean haveRaParams = (mRaParams != null);
- boolean shouldSendRA = false;
-
- try {
- putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute,
- haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT);
- putSlla(ra, mInterface.macAddr.toByteArray());
- mRaLength = ra.position();
-
- // https://tools.ietf.org/html/rfc5175#section-4 says:
- //
- // "MUST NOT be added to a Router Advertisement message
- // if no flags in the option are set."
- //
- // putExpandedFlagsOption(ra);
-
- if (haveRaParams) {
- putMtu(ra, mRaParams.mtu);
- mRaLength = ra.position();
-
- for (IpPrefix ipp : mRaParams.prefixes) {
- putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
- mRaLength = ra.position();
- shouldSendRA = true;
- }
-
- if (mRaParams.dnses.size() > 0) {
- putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
- mRaLength = ra.position();
- shouldSendRA = true;
- }
- }
-
- for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
- putPio(ra, ipp, 0, 0);
- mRaLength = ra.position();
- shouldSendRA = true;
- }
-
- final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses();
- if (!deprecatedDnses.isEmpty()) {
- putRdnss(ra, deprecatedDnses, 0);
- mRaLength = ra.position();
- shouldSendRA = true;
- }
- } catch (BufferOverflowException e) {
- // The packet up to mRaLength is valid, since it has been updated
- // progressively as the RA was built. Log an error, and continue
- // on as best as possible.
- Log.e(TAG, "Could not construct new RA: " + e);
- }
-
- // We have nothing worth announcing; indicate as much to maybeSendRA().
- if (!shouldSendRA) {
- mRaLength = 0;
- }
- }
-
- private void maybeNotifyMulticastTransmitter() {
- final MulticastTransmitter m = mMulticastTransmitter;
- if (m != null) {
- m.hup();
- }
- }
-
- private static byte asByte(int value) {
- return (byte) value;
- }
- private static short asShort(int value) {
- return (short) value;
- }
-
- private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
- /**
- Router Advertisement Message Format
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Code | Checksum |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Cur Hop Limit |M|O|H|Prf|P|R|R| Router Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Reachable Time |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Retrans Timer |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Options ...
- +-+-+-+-+-+-+-+-+-+-+-+-
- */
- ra.put(ICMPV6_ND_ROUTER_ADVERT)
- .put(asByte(0))
- .putShort(asShort(0))
- .put(hopLimit)
- // RFC 4191 "high" preference, iff. advertising a default route.
- .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
- .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
- .putInt(0)
- .putInt(0);
- }
-
- private static void putSlla(ByteBuffer ra, byte[] slla) {
- /**
- Source/Target Link-layer Address
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Link-Layer Address ...
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- if (slla == null || slla.length != 6) {
- // Only IEEE 802.3 6-byte addresses are supported.
- return;
- }
-
- final byte nd_option_slla = 1;
- final byte slla_num_8octets = 1;
- ra.put(nd_option_slla)
- .put(slla_num_8octets)
- .put(slla);
- }
-
- private static void putExpandedFlagsOption(ByteBuffer ra) {
- /**
- Router Advertisement Expanded Flags Option
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Bit fields available ..
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- ... for assignment |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
-
- final byte nd_option__efo = 26;
- final byte efo_num_8octets = 1;
-
- ra.put(nd_option__efo)
- .put(efo_num_8octets)
- .putShort(asShort(0))
- .putInt(0);
- }
-
- private static void putMtu(ByteBuffer ra, int mtu) {
- /**
- MTU
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Reserved |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | MTU |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- final byte nd_option_mtu = 5;
- final byte mtu_num_8octs = 1;
- ra.put(nd_option_mtu)
- .put(mtu_num_8octs)
- .putShort(asShort(0))
- .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
- }
-
- private static void putPio(ByteBuffer ra, IpPrefix ipp,
- int validTime, int preferredTime) {
- /**
- Prefix Information
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Prefix Length |L|A| Reserved1 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Valid Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Preferred Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Reserved2 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | |
- + +
- | |
- + Prefix +
- | |
- + +
- | |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- final int prefixLength = ipp.getPrefixLength();
- if (prefixLength != 64) {
- return;
- }
- final byte nd_option_pio = 3;
- final byte pio_num_8octets = 4;
-
- if (validTime < 0) validTime = 0;
- if (preferredTime < 0) preferredTime = 0;
- if (preferredTime > validTime) preferredTime = validTime;
-
- final byte[] addr = ipp.getAddress().getAddress();
- ra.put(nd_option_pio)
- .put(pio_num_8octets)
- .put(asByte(prefixLength))
- .put(asByte(0xc0)) /* L & A set */
- .putInt(validTime)
- .putInt(preferredTime)
- .putInt(0)
- .put(addr);
- }
-
- private static void putRio(ByteBuffer ra, IpPrefix ipp) {
- /**
- Route Information Option
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Prefix Length |Resvd|Prf|Resvd|
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Route Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Prefix (Variable Length) |
- . .
- . .
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- final int prefixLength = ipp.getPrefixLength();
- if (prefixLength > 64) {
- return;
- }
- final byte nd_option_rio = 24;
- final byte rio_num_8octets = asByte(
- (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
-
- final byte[] addr = ipp.getAddress().getAddress();
- ra.put(nd_option_rio)
- .put(rio_num_8octets)
- .put(asByte(prefixLength))
- .put(asByte(0x18))
- .putInt(DEFAULT_LIFETIME);
-
- // Rely upon an IpPrefix's address being properly zeroed.
- if (prefixLength > 0) {
- ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16);
- }
- }
-
- private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
- /**
- Recursive DNS Server (RDNSS) Option
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Type | Length | Reserved |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Lifetime |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | |
- : Addresses of IPv6 Recursive DNS Servers :
- | |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
-
- final HashSet<Inet6Address> filteredDnses = new HashSet<>();
- for (Inet6Address dns : dnses) {
- if ((new LinkAddress(dns, RFC7421_PREFIX_LENGTH)).isGlobalPreferred()) {
- filteredDnses.add(dns);
- }
- }
- if (filteredDnses.isEmpty()) return;
-
- final byte nd_option_rdnss = 25;
- final byte rdnss_num_8octets = asByte(dnses.size() * 2 + 1);
- ra.put(nd_option_rdnss)
- .put(rdnss_num_8octets)
- .putShort(asShort(0))
- .putInt(lifetime);
-
- for (Inet6Address dns : filteredDnses) {
- // NOTE: If the full of list DNS servers doesn't fit in the packet,
- // this code will cause a buffer overflow and the RA won't include
- // this instance of the option at all.
- //
- // TODO: Consider looking at ra.remaining() to determine how many
- // DNS servers will fit, and adding only those.
- ra.put(dns.getAddress());
- }
- }
-
- private boolean createSocket() {
- final int send_timout_ms = 300;
-
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR);
- try {
- mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
- // Setting SNDTIMEO is purely for defensive purposes.
- Os.setsockoptTimeval(
- mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
- SocketUtils.bindSocketToInterface(mSocket, mInterface.name);
- TetheringUtils.setupRaSocket(mSocket, mInterface.index);
- } catch (ErrnoException | IOException e) {
- Log.e(TAG, "Failed to create RA daemon socket: " + e);
- return false;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
-
- return true;
- }
-
- private void closeSocket() {
- if (mSocket != null) {
- try {
- SocketUtils.closeSocket(mSocket);
- } catch (IOException ignored) { }
- }
- mSocket = null;
- }
-
- private boolean isSocketValid() {
- final FileDescriptor s = mSocket;
- return (s != null) && s.valid();
- }
-
- private boolean isSuitableDestination(InetSocketAddress dest) {
- if (mAllNodes.equals(dest)) {
- return true;
- }
-
- final InetAddress destip = dest.getAddress();
- return (destip instanceof Inet6Address)
- && destip.isLinkLocalAddress()
- && (((Inet6Address) destip).getScopeId() == mInterface.index);
- }
-
- private void maybeSendRA(InetSocketAddress dest) {
- if (dest == null || !isSuitableDestination(dest)) {
- dest = mAllNodes;
- }
-
- try {
- synchronized (mLock) {
- if (mRaLength < MIN_RA_HEADER_SIZE) {
- // No actual RA to send.
- return;
- }
- Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest);
- }
- Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress());
- } catch (ErrnoException | SocketException e) {
- if (isSocketValid()) {
- Log.e(TAG, "sendto error: " + e);
- }
- }
- }
-
- private final class UnicastResponder extends Thread {
- private final InetSocketAddress mSolicitor = new InetSocketAddress(0);
- // The recycled buffer for receiving Router Solicitations from clients.
- // If the RS is larger than IPV6_MIN_MTU the packets are truncated.
- // This is fine since currently only byte 0 is examined anyway.
- private final byte[] mSolicitation = new byte[IPV6_MIN_MTU];
-
- @Override
- public void run() {
- while (isSocketValid()) {
- try {
- // Blocking receive.
- final int rval = Os.recvfrom(
- mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor);
- // Do the least possible amount of validation.
- if (rval < 1 || mSolicitation[0] != ICMPV6_ND_ROUTER_SOLICIT) {
- continue;
- }
- } catch (ErrnoException | SocketException e) {
- if (isSocketValid()) {
- Log.e(TAG, "recvfrom error: " + e);
- }
- continue;
- }
-
- maybeSendRA(mSolicitor);
- }
- }
- }
-
- // TODO: Consider moving this to run on a provided Looper as a Handler,
- // with WakeupMessage-style messages providing the timer driven input.
- private final class MulticastTransmitter extends Thread {
- private final Random mRandom = new Random();
- private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0);
-
- @Override
- public void run() {
- while (isSocketValid()) {
- try {
- Thread.sleep(getNextMulticastTransmitDelayMs());
- } catch (InterruptedException ignored) {
- // Stop sleeping, immediately send an RA, and continue.
- }
-
- maybeSendRA(mAllNodes);
- synchronized (mLock) {
- if (mDeprecatedInfoTracker.decrementCounters()) {
- // At least one deprecated PIO has been removed;
- // reassemble the RA.
- assembleRaLocked();
- }
- }
- }
- }
-
- public void hup() {
- // Set to one fewer that the desired number, because as soon as
- // the thread interrupt is processed we immediately send an RA
- // and mUrgentAnnouncements is not examined until the subsequent
- // sleep interval computation (i.e. this way we send 3 and not 4).
- mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1);
- interrupt();
- }
-
- private int getNextMulticastTransmitDelaySec() {
- boolean deprecationInProgress = false;
- synchronized (mLock) {
- if (mRaLength < MIN_RA_HEADER_SIZE) {
- // No actual RA to send; just sleep for 1 day.
- return DAY_IN_SECONDS;
- }
- deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
- }
-
- final int urgentPending = mUrgentAnnouncements.getAndDecrement();
- if ((urgentPending > 0) || deprecationInProgress) {
- return MIN_DELAY_BETWEEN_RAS_SEC;
- }
-
- return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt(
- MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC);
- }
-
- private long getNextMulticastTransmitDelayMs() {
- return 1000 * (long) getNextMulticastTransmitDelaySec();
- }
- }
-}
diff --git a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
deleted file mode 100644
index b1ffdb01f5f3..000000000000
--- a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
+++ /dev/null
@@ -1,75 +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.net.util;
-
-import android.net.INetdUnsolicitedEventListener;
-
-import androidx.annotation.NonNull;
-
-/**
- * Base {@link INetdUnsolicitedEventListener} that provides no-op implementations which can be
- * overridden.
- */
-public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
-
- @Override
- public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs,
- int uid) { }
-
- @Override
- public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { }
-
- @Override
- public void onInterfaceDnsServerInfo(@NonNull String ifName, long lifetimeS,
- @NonNull String[] servers) { }
-
- @Override
- public void onInterfaceAddressUpdated(@NonNull String addr, String ifName, int flags,
- int scope) { }
-
- @Override
- public void onInterfaceAddressRemoved(@NonNull String addr, @NonNull String ifName, int flags,
- int scope) { }
-
- @Override
- public void onInterfaceAdded(@NonNull String ifName) { }
-
- @Override
- public void onInterfaceRemoved(@NonNull String ifName) { }
-
- @Override
- public void onInterfaceChanged(@NonNull String ifName, boolean up) { }
-
- @Override
- public void onInterfaceLinkStateChanged(@NonNull String ifName, boolean up) { }
-
- @Override
- public void onRouteChanged(boolean updated, @NonNull String route, @NonNull String gateway,
- @NonNull String ifName) { }
-
- @Override
- public void onStrictCleartextDetected(int uid, @NonNull String hex) { }
-
- @Override
- public int getInterfaceVersion() {
- return INetdUnsolicitedEventListener.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return INetdUnsolicitedEventListener.HASH;
- }
-}
diff --git a/packages/Tethering/src/android/net/util/InterfaceSet.java b/packages/Tethering/src/android/net/util/InterfaceSet.java
deleted file mode 100644
index 758978711bd4..000000000000
--- a/packages/Tethering/src/android/net/util/InterfaceSet.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.StringJoiner;
-
-
-/**
- * @hide
- */
-public class InterfaceSet {
- public final Set<String> ifnames;
-
- public InterfaceSet(String... names) {
- final Set<String> nameSet = new HashSet<>();
- for (String name : names) {
- if (name != null) nameSet.add(name);
- }
- ifnames = Collections.unmodifiableSet(nameSet);
- }
-
- @Override
- public String toString() {
- final StringJoiner sj = new StringJoiner(",", "[", "]");
- for (String ifname : ifnames) sj.add(ifname);
- return sj.toString();
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj != null
- && obj instanceof InterfaceSet
- && ifnames.equals(((InterfaceSet) obj).ifnames);
- }
-}
diff --git a/packages/Tethering/src/android/net/util/PrefixUtils.java b/packages/Tethering/src/android/net/util/PrefixUtils.java
deleted file mode 100644
index f203e9995f3d..000000000000
--- a/packages/Tethering/src/android/net/util/PrefixUtils.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-
-/**
- * @hide
- */
-public class PrefixUtils {
- private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = {
- pfx("127.0.0.0/8"), // IPv4 loopback
- pfx("169.254.0.0/16"), // IPv4 link-local, RFC3927#section-8
- pfx("::/3"),
- pfx("fe80::/64"), // IPv6 link-local
- pfx("fc00::/7"), // IPv6 ULA
- pfx("ff02::/8"), // IPv6 link-local multicast
- };
-
- public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24");
-
- /** Get non forwardable prefixes. */
- public static Set<IpPrefix> getNonForwardablePrefixes() {
- final HashSet<IpPrefix> prefixes = new HashSet<>();
- addNonForwardablePrefixes(prefixes);
- return prefixes;
- }
-
- /** Add non forwardable prefixes. */
- public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) {
- Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES);
- }
-
- /** Get local prefixes from |lp|. */
- public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) {
- final HashSet<IpPrefix> localPrefixes = new HashSet<>();
- if (lp == null) return localPrefixes;
-
- for (LinkAddress addr : lp.getAllLinkAddresses()) {
- if (addr.getAddress().isLinkLocalAddress()) continue;
- localPrefixes.add(asIpPrefix(addr));
- }
- // TODO: Add directly-connected routes as well (ones from which we did
- // not also form a LinkAddress)?
-
- return localPrefixes;
- }
-
- /** Convert LinkAddress |addr| to IpPrefix. */
- public static IpPrefix asIpPrefix(LinkAddress addr) {
- return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
- }
-
- /** Convert InetAddress |ip| to IpPrefix. */
- public static IpPrefix ipAddressAsPrefix(InetAddress ip) {
- final int bitLength = (ip instanceof Inet4Address)
- ? NetworkConstants.IPV4_ADDR_BITS
- : NetworkConstants.IPV6_ADDR_BITS;
- return new IpPrefix(ip, bitLength);
- }
-
- private static IpPrefix pfx(String prefixStr) {
- return new IpPrefix(prefixStr);
- }
-}
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
deleted file mode 100644
index 53b54f7de05d..000000000000
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ /dev/null
@@ -1,165 +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.net.util;
-
-import android.net.TetherStatsParcel;
-import android.net.TetheringRequestParcel;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import java.io.FileDescriptor;
-import java.net.Inet6Address;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * The classes and the methods for tethering utilization.
- *
- * {@hide}
- */
-public class TetheringUtils {
- public static final byte[] ALL_NODES = new byte[] {
- (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
- };
-
- /**
- * Configures a socket for receiving and sending ICMPv6 neighbor advertisments.
- * @param fd the socket's {@link FileDescriptor}.
- */
- public static native void setupNaSocket(FileDescriptor fd)
- throws SocketException;
-
- /**
- * Configures a socket for receiving and sending ICMPv6 neighbor solicitations.
- * @param fd the socket's {@link FileDescriptor}.
- */
- public static native void setupNsSocket(FileDescriptor fd)
- throws SocketException;
-
- /**
- * The object which records offload Tx/Rx forwarded bytes/packets.
- * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with
- * this class as well.
- */
- public static class ForwardedStats {
- public final long rxBytes;
- public final long rxPackets;
- public final long txBytes;
- public final long txPackets;
-
- public ForwardedStats() {
- rxBytes = 0;
- rxPackets = 0;
- txBytes = 0;
- txPackets = 0;
- }
-
- public ForwardedStats(long rxBytes, long txBytes) {
- this.rxBytes = rxBytes;
- this.rxPackets = 0;
- this.txBytes = txBytes;
- this.txPackets = 0;
- }
-
- public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) {
- this.rxBytes = rxBytes;
- this.rxPackets = rxPackets;
- this.txBytes = txBytes;
- this.txPackets = txPackets;
- }
-
- public ForwardedStats(@NonNull TetherStatsParcel tetherStats) {
- rxBytes = tetherStats.rxBytes;
- rxPackets = tetherStats.rxPackets;
- txBytes = tetherStats.txBytes;
- txPackets = tetherStats.txPackets;
- }
-
- public ForwardedStats(@NonNull ForwardedStats other) {
- rxBytes = other.rxBytes;
- rxPackets = other.rxPackets;
- txBytes = other.txBytes;
- txPackets = other.txPackets;
- }
-
- /** Add Tx/Rx bytes/packets and return the result as a new object. */
- @NonNull
- public ForwardedStats add(@NonNull ForwardedStats other) {
- return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets,
- txBytes + other.txBytes, txPackets + other.txPackets);
- }
-
- /** Subtract Tx/Rx bytes/packets and return the result as a new object. */
- @NonNull
- public ForwardedStats subtract(@NonNull ForwardedStats other) {
- // TODO: Perhaps throw an exception if any negative difference value just in case.
- final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0);
- final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0);
- final long txBytesDiff = Math.max(txBytes - other.txBytes, 0);
- final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0);
- return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff);
- }
-
- /** Returns the string representation of this object. */
- @NonNull
- public String toString() {
- return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes,
- rxPackets, txBytes, txPackets);
- }
- }
-
- /**
- * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
- * @param fd the socket's {@link FileDescriptor}.
- * @param ifIndex the interface index.
- */
- public static native void setupRaSocket(FileDescriptor fd, int ifIndex)
- throws SocketException;
-
- /**
- * Read s as an unsigned 16-bit integer.
- */
- public static int uint16(short s) {
- return s & 0xffff;
- }
-
- /** Check whether two TetheringRequestParcels are the same. */
- public static boolean isTetheringRequestEquals(final TetheringRequestParcel request,
- final TetheringRequestParcel otherRequest) {
- if (request == otherRequest) return true;
-
- return request != null && otherRequest != null
- && request.tetheringType == otherRequest.tetheringType
- && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address)
- && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress)
- && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
- && request.showProvisioningUi == otherRequest.showProvisioningUi;
- }
-
- /** Get inet6 address for all nodes given scope ID. */
- public static Inet6Address getAllNodesForScopeId(int scopeId) {
- try {
- return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
- } catch (UnknownHostException uhe) {
- Log.wtf("TetheringUtils", "Failed to construct Inet6Address from "
- + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId);
- return null;
- }
- }
-}
diff --git a/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java b/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java
deleted file mode 100644
index e2804abd752b..000000000000
--- a/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-import android.util.Log;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-
-/**
- * A utility class that runs the provided callback on the provided handler when
- * intents matching the provided filter arrive. Intents received by a stale
- * receiver are safely ignored.
- *
- * Calls to startListening() and stopListening() must happen on the same thread.
- *
- * @hide
- */
-public class VersionedBroadcastListener {
- private static final boolean DBG = false;
-
- private final String mTag;
- private final Context mContext;
- private final Handler mHandler;
- private final IntentFilter mFilter;
- private final Consumer<Intent> mCallback;
- private final AtomicInteger mGenerationNumber;
- private BroadcastReceiver mReceiver;
-
- public VersionedBroadcastListener(String tag, Context ctx, Handler handler,
- IntentFilter filter, Consumer<Intent> callback) {
- mTag = tag;
- mContext = ctx;
- mHandler = handler;
- mFilter = filter;
- mCallback = callback;
- mGenerationNumber = new AtomicInteger(0);
- }
-
- /** Start listening to intent broadcast. */
- public void startListening() {
- if (DBG) Log.d(mTag, "startListening");
- if (mReceiver != null) return;
-
- mReceiver = new Receiver(mTag, mGenerationNumber, mCallback);
- mContext.registerReceiver(mReceiver, mFilter, null, mHandler);
- }
-
- /** Stop listening to intent broadcast. */
- public void stopListening() {
- if (DBG) Log.d(mTag, "stopListening");
- if (mReceiver == null) return;
-
- mGenerationNumber.incrementAndGet();
- mContext.unregisterReceiver(mReceiver);
- mReceiver = null;
- }
-
- private static class Receiver extends BroadcastReceiver {
- public final String tag;
- public final AtomicInteger atomicGenerationNumber;
- public final Consumer<Intent> callback;
- // Used to verify this receiver is still current.
- public final int generationNumber;
-
- Receiver(String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) {
- this.tag = tag;
- this.atomicGenerationNumber = atomicGenerationNumber;
- this.callback = callback;
- generationNumber = atomicGenerationNumber.incrementAndGet();
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final int currentGenerationNumber = atomicGenerationNumber.get();
-
- if (DBG) {
- Log.d(tag, "receiver generationNumber=" + generationNumber
- + ", current generationNumber=" + currentGenerationNumber);
- }
- if (generationNumber != currentGenerationNumber) return;
-
- callback.accept(intent);
- }
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
deleted file mode 100644
index 20f30ea7a460..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ /dev/null
@@ -1,778 +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.networkstack.tethering;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStats.UID_TETHERING;
-import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
-
-import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
-
-import android.app.usage.NetworkStatsManager;
-import android.net.INetd;
-import android.net.MacAddress;
-import android.net.NetworkStats;
-import android.net.NetworkStats.Entry;
-import android.net.TetherOffloadRuleParcel;
-import android.net.TetherStatsParcel;
-import android.net.ip.IpServer;
-import android.net.netstats.provider.NetworkStatsProvider;
-import android.net.util.SharedLog;
-import android.net.util.TetheringUtils.ForwardedStats;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.net.Inet6Address;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * This coordinator is responsible for providing BPF offload relevant functionality.
- * - Get tethering stats.
- * - Set data limit.
- * - Set global alert.
- * - Add/remove forwarding rules.
- *
- * @hide
- */
-public class BpfCoordinator {
- private static final String TAG = BpfCoordinator.class.getSimpleName();
- private static final int DUMP_TIMEOUT_MS = 10_000;
-
- @VisibleForTesting
- enum StatsType {
- STATS_PER_IFACE,
- STATS_PER_UID,
- }
-
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final INetd mNetd;
- @NonNull
- private final SharedLog mLog;
- @NonNull
- private final Dependencies mDeps;
- @Nullable
- private final BpfTetherStatsProvider mStatsProvider;
-
- // True if BPF offload is supported, false otherwise. The BPF offload could be disabled by
- // a runtime resource overlay package or device configuration. This flag is only initialized
- // in the constructor because it is hard to unwind all existing change once device
- // configuration is changed. Especially the forwarding rules. Keep the same setting
- // to make it simpler. See also TetheringConfiguration.
- private final boolean mIsBpfEnabled;
-
- // Tracks whether BPF tethering is started or not. This is set by tethering before it
- // starts the first IpServer and is cleared by tethering shortly before the last IpServer
- // is stopped. Note that rule updates (especially deletions, but sometimes additions as
- // well) may arrive when this is false. If they do, they must be communicated to netd.
- // Changes in data limits may also arrive when this is false, and if they do, they must
- // also be communicated to netd.
- private boolean mPollingStarted = false;
-
- // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
- // quota is interface independent and global for tether offload.
- private long mRemainingAlertQuota = QUOTA_UNLIMITED;
-
- // Maps upstream interface index to offloaded traffic statistics.
- // Always contains the latest total bytes/packets, since each upstream was started, received
- // from the BPF maps for each interface.
- private final SparseArray<ForwardedStats> mStats = new SparseArray<>();
-
- // Maps upstream interface names to interface quotas.
- // Always contains the latest value received from the framework for each interface, regardless
- // of whether offload is currently running (or is even supported) on that interface. Only
- // includes interfaces that have a quota set. Note that this map is used for storing the quota
- // which is set from the service. Because the service uses the interface name to present the
- // interface, this map uses the interface name to be the mapping index.
- private final HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
-
- // Maps upstream interface index to interface names.
- // Store all interface name since boot. Used for lookup what interface name it is from the
- // tether stats got from netd because netd reports interface index to present an interface.
- // TODO: Remove the unused interface name.
- private final SparseArray<String> mInterfaceNames = new SparseArray<>();
-
- // Map of downstream rule maps. Each of these maps represents the IPv6 forwarding rules for a
- // given downstream. Each map:
- // - Is owned by the IpServer that is responsible for that downstream.
- // - Must only be modified by that IpServer.
- // - Is created when the IpServer adds its first rule, and deleted when the IpServer deletes
- // its last rule (or clears its rules).
- // TODO: Perhaps seal the map and rule operations which communicates with netd into a class.
- // TODO: Does this need to be a LinkedHashMap or can it just be a HashMap? Also, could it be
- // a ConcurrentHashMap, in order to avoid the copies in tetherOffloadRuleClear
- // and tetherOffloadRuleUpdate?
- // TODO: Perhaps use one-dimensional map and access specific downstream rules via downstream
- // index. For doing that, IpServer must guarantee that it always has a valid IPv6 downstream
- // interface index while calling function to clear all rules. IpServer may be calling clear
- // rules function without a valid IPv6 downstream interface index even if it may have one
- // before. IpServer would need to call getInterfaceParams() in the constructor instead of when
- // startIpv6() is called, and make mInterfaceParams final.
- private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
- mIpv6ForwardingRules = new LinkedHashMap<>();
-
- // Runnable that used by scheduling next polling of stats.
- private final Runnable mScheduledPollingTask = () -> {
- updateForwardedStatsFromNetd();
- maybeSchedulePollingStats();
- };
-
- @VisibleForTesting
- public abstract static class Dependencies {
- /** Get handler. */
- @NonNull public abstract Handler getHandler();
-
- /** Get netd. */
- @NonNull public abstract INetd getNetd();
-
- /** Get network stats manager. */
- @NonNull public abstract NetworkStatsManager getNetworkStatsManager();
-
- /** Get shared log. */
- @NonNull public abstract SharedLog getSharedLog();
-
- /** Get tethering configuration. */
- @Nullable public abstract TetheringConfiguration getTetherConfig();
- }
-
- @VisibleForTesting
- public BpfCoordinator(@NonNull Dependencies deps) {
- mDeps = deps;
- mHandler = mDeps.getHandler();
- mNetd = mDeps.getNetd();
- mLog = mDeps.getSharedLog().forSubComponent(TAG);
- mIsBpfEnabled = isBpfEnabled();
- BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
- try {
- mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
- getClass().getSimpleName(), provider);
- } catch (RuntimeException e) {
- // TODO: Perhaps not allow to use BPF offload because the reregistration failure
- // implied that no data limit could be applies on a metered upstream if any.
- Log.wtf(TAG, "Cannot register offload stats provider: " + e);
- provider = null;
- }
- mStatsProvider = provider;
- }
-
- /**
- * Start BPF tethering offload stats polling when the first upstream is started.
- * Note that this can be only called on handler thread.
- * TODO: Perhaps check BPF support before starting.
- * TODO: Start the stats polling only if there is any client on the downstream.
- */
- public void startPolling() {
- if (mPollingStarted) return;
-
- if (!mIsBpfEnabled) {
- mLog.i("Offload disabled");
- return;
- }
-
- mPollingStarted = true;
- maybeSchedulePollingStats();
-
- mLog.i("Polling started");
- }
-
- /**
- * Stop BPF tethering offload stats polling.
- * The data limit cleanup and the tether stats maps cleanup are not implemented here.
- * These cleanups rely on all IpServers calling #tetherOffloadRuleRemove. After the
- * last rule is removed from the upstream, #tetherOffloadRuleRemove does the cleanup
- * functionality.
- * Note that this can be only called on handler thread.
- */
- public void stopPolling() {
- if (!mPollingStarted) return;
-
- // Stop scheduled polling tasks and poll the latest stats from BPF maps.
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
- }
- updateForwardedStatsFromNetd();
- mPollingStarted = false;
-
- mLog.i("Polling stopped");
- }
-
- /**
- * Add forwarding rule. After adding the first rule on a given upstream, must add the data
- * limit on the given upstream.
- * Note that this can be only called on handler thread.
- */
- public void tetherOffloadRuleAdd(
- @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
- if (!mIsBpfEnabled) return;
-
- try {
- // TODO: Perhaps avoid to add a duplicate rule.
- mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Could not add IPv6 forwarding rule: ", e);
- return;
- }
-
- if (!mIpv6ForwardingRules.containsKey(ipServer)) {
- mIpv6ForwardingRules.put(ipServer, new LinkedHashMap<Inet6Address,
- Ipv6ForwardingRule>());
- }
- LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
-
- // Setup the data limit on the given upstream if the first rule is added.
- final int upstreamIfindex = rule.upstreamIfindex;
- if (!isAnyRuleOnUpstream(upstreamIfindex)) {
- // If failed to set a data limit, probably should not use this upstream, because
- // the upstream may not want to blow through the data limit that was told to apply.
- // TODO: Perhaps stop the coordinator.
- boolean success = updateDataLimit(upstreamIfindex);
- if (!success) {
- final String iface = mInterfaceNames.get(upstreamIfindex);
- mLog.e("Setting data limit for " + iface + " failed.");
- }
- }
-
- // Must update the adding rule after calling #isAnyRuleOnUpstream because it needs to
- // check if it is about adding a first rule for a given upstream.
- rules.put(rule.address, rule);
- }
-
- /**
- * Remove forwarding rule. After removing the last rule on a given upstream, must clear
- * data limit, update the last tether stats and remove the tether stats in the BPF maps.
- * Note that this can be only called on handler thread.
- */
- public void tetherOffloadRuleRemove(
- @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
- if (!mIsBpfEnabled) return;
-
- try {
- // TODO: Perhaps avoid to remove a non-existent rule.
- mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Could not remove IPv6 forwarding rule: ", e);
- return;
- }
-
- LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
- if (rules == null) return;
-
- // Must remove rules before calling #isAnyRuleOnUpstream because it needs to check if
- // the last rule is removed for a given upstream. If no rule is removed, return early.
- // Avoid unnecessary work on a non-existent rule which may have never been added or
- // removed already.
- if (rules.remove(rule.address) == null) return;
-
- // Remove the downstream entry if it has no more rule.
- if (rules.isEmpty()) {
- mIpv6ForwardingRules.remove(ipServer);
- }
-
- // Do cleanup functionality if there is no more rule on the given upstream.
- final int upstreamIfindex = rule.upstreamIfindex;
- if (!isAnyRuleOnUpstream(upstreamIfindex)) {
- try {
- final TetherStatsParcel stats =
- mNetd.tetherOffloadGetAndClearStats(upstreamIfindex);
- // Update the last stats delta and delete the local cache for a given upstream.
- updateQuotaAndStatsFromSnapshot(new TetherStatsParcel[] {stats});
- mStats.remove(upstreamIfindex);
- } catch (RemoteException | ServiceSpecificException e) {
- Log.wtf(TAG, "Exception when cleanup tether stats for upstream index "
- + upstreamIfindex + ": ", e);
- }
- }
- }
-
- /**
- * Clear all forwarding rules for a given downstream.
- * Note that this can be only called on handler thread.
- */
- public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) {
- if (!mIsBpfEnabled) return;
-
- final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
- ipServer);
- if (rules == null) return;
-
- // Need to build a rule list because the rule map may be changed in the iteration.
- for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) {
- tetherOffloadRuleRemove(ipServer, rule);
- }
- }
-
- /**
- * Update existing forwarding rules to new upstream for a given downstream.
- * Note that this can be only called on handler thread.
- */
- public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) {
- if (!mIsBpfEnabled) return;
-
- final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
- ipServer);
- if (rules == null) return;
-
- // Need to build a rule list because the rule map may be changed in the iteration.
- for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) {
- // Remove the old rule before adding the new one because the map uses the same key for
- // both rules. Reversing the processing order causes that the new rule is removed as
- // unexpected.
- // TODO: Add new rule first to reduce the latency which has no rule.
- tetherOffloadRuleRemove(ipServer, rule);
- tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex));
- }
- }
-
- /**
- * Add upstream name to lookup table. The lookup table is used for tether stats interface name
- * lookup because the netd only reports interface index in BPF tether stats but the service
- * expects the interface name in NetworkStats object.
- * Note that this can be only called on handler thread.
- */
- public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) {
- if (!mIsBpfEnabled) return;
-
- if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return;
-
- // The same interface index to name mapping may be added by different IpServer objects or
- // re-added by reconnection on the same upstream interface. Ignore the duplicate one.
- final String iface = mInterfaceNames.get(upstreamIfindex);
- if (iface == null) {
- mInterfaceNames.put(upstreamIfindex, upstreamIface);
- } else if (!TextUtils.equals(iface, upstreamIface)) {
- Log.wtf(TAG, "The upstream interface name " + upstreamIface
- + " is different from the existing interface name "
- + iface + " for index " + upstreamIfindex);
- }
- }
-
- /**
- * Dump information.
- * Block the function until all the data are dumped on the handler thread or timed-out. The
- * reason is that dumpsys invokes this function on the thread of caller and the data may only
- * be allowed to be accessed on the handler thread.
- */
- public void dump(@NonNull IndentingPrintWriter pw) {
- final ConditionVariable dumpDone = new ConditionVariable();
- mHandler.post(() -> {
- pw.println("mIsBpfEnabled: " + mIsBpfEnabled);
- pw.println("Polling " + (mPollingStarted ? "started" : "not started"));
- pw.println("Stats provider " + (mStatsProvider != null
- ? "registered" : "not registered"));
- pw.println("Upstream quota: " + mInterfaceQuotas.toString());
- pw.println("Polling interval: " + getPollingInterval() + " ms");
-
- pw.println("Forwarding stats:");
- pw.increaseIndent();
- if (mStats.size() == 0) {
- pw.println("<empty>");
- } else {
- dumpStats(pw);
- }
- pw.decreaseIndent();
-
- pw.println("Forwarding rules:");
- pw.increaseIndent();
- if (mIpv6ForwardingRules.size() == 0) {
- pw.println("<empty>");
- } else {
- dumpIpv6ForwardingRules(pw);
- }
- pw.decreaseIndent();
-
- dumpDone.open();
- });
- if (!dumpDone.block(DUMP_TIMEOUT_MS)) {
- pw.println("... dump timed-out after " + DUMP_TIMEOUT_MS + "ms");
- }
- }
-
- private void dumpStats(@NonNull IndentingPrintWriter pw) {
- for (int i = 0; i < mStats.size(); i++) {
- final int upstreamIfindex = mStats.keyAt(i);
- final ForwardedStats stats = mStats.get(upstreamIfindex);
- pw.println(String.format("%d(%s) - %s", upstreamIfindex, mInterfaceNames.get(
- upstreamIfindex), stats.toString()));
- }
- }
-
- private void dumpIpv6ForwardingRules(@NonNull IndentingPrintWriter pw) {
- for (Map.Entry<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>> entry :
- mIpv6ForwardingRules.entrySet()) {
- IpServer ipServer = entry.getKey();
- // The rule downstream interface index is paired with the interface name from
- // IpServer#interfaceName. See #startIPv6, #updateIpv6ForwardingRules in IpServer.
- final String downstreamIface = ipServer.interfaceName();
- pw.println("[" + downstreamIface + "]: iif(iface) oif(iface) v6addr srcmac dstmac");
-
- pw.increaseIndent();
- LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = entry.getValue();
- for (Ipv6ForwardingRule rule : rules.values()) {
- final int upstreamIfindex = rule.upstreamIfindex;
- pw.println(String.format("%d(%s) %d(%s) %s %s %s", upstreamIfindex,
- mInterfaceNames.get(upstreamIfindex), rule.downstreamIfindex,
- downstreamIface, rule.address, rule.srcMac, rule.dstMac));
- }
- pw.decreaseIndent();
- }
- }
-
- /** IPv6 forwarding rule class. */
- public static class Ipv6ForwardingRule {
- public final int upstreamIfindex;
- public final int downstreamIfindex;
-
- @NonNull
- public final Inet6Address address;
- @NonNull
- public final MacAddress srcMac;
- @NonNull
- public final MacAddress dstMac;
-
- public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex,
- @NonNull Inet6Address address, @NonNull MacAddress srcMac,
- @NonNull MacAddress dstMac) {
- this.upstreamIfindex = upstreamIfindex;
- this.downstreamIfindex = downstreamIfIndex;
- this.address = address;
- this.srcMac = srcMac;
- this.dstMac = dstMac;
- }
-
- /** Return a new rule object which updates with new upstream index. */
- @NonNull
- public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
- return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
- dstMac);
- }
-
- /**
- * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
- * would be error-prone due to generated stable AIDL classes not having a copy constructor.
- */
- @NonNull
- public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
- final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
- parcel.inputInterfaceIndex = upstreamIfindex;
- parcel.outputInterfaceIndex = downstreamIfindex;
- parcel.destination = address.getAddress();
- parcel.prefixLength = 128;
- parcel.srcL2Address = srcMac.toByteArray();
- parcel.dstL2Address = dstMac.toByteArray();
- return parcel;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof Ipv6ForwardingRule)) return false;
- Ipv6ForwardingRule that = (Ipv6ForwardingRule) o;
- return this.upstreamIfindex == that.upstreamIfindex
- && this.downstreamIfindex == that.downstreamIfindex
- && Objects.equals(this.address, that.address)
- && Objects.equals(this.srcMac, that.srcMac)
- && Objects.equals(this.dstMac, that.dstMac);
- }
-
- @Override
- public int hashCode() {
- // TODO: if this is ever used in production code, don't pass ifindices
- // to Objects.hash() to avoid autoboxing overhead.
- return Objects.hash(upstreamIfindex, downstreamIfindex, address, srcMac, dstMac);
- }
- }
-
- /**
- * A BPF tethering stats provider to provide network statistics to the system.
- * Note that this class' data may only be accessed on the handler thread.
- */
- @VisibleForTesting
- class BpfTetherStatsProvider extends NetworkStatsProvider {
- // The offloaded traffic statistics per interface that has not been reported since the
- // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams
- // and has pending tether stats delta are included in this NetworkStats object.
- private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
-
- // The same stats as above, but counts network stats per uid.
- private NetworkStats mUidStats = new NetworkStats(0L, 0);
-
- @Override
- public void onRequestStatsUpdate(int token) {
- mHandler.post(() -> pushTetherStats());
- }
-
- @Override
- public void onSetAlert(long quotaBytes) {
- mHandler.post(() -> updateAlertQuota(quotaBytes));
- }
-
- @Override
- public void onSetLimit(@NonNull String iface, long quotaBytes) {
- if (quotaBytes < QUOTA_UNLIMITED) {
- throw new IllegalArgumentException("invalid quota value " + quotaBytes);
- }
-
- mHandler.post(() -> {
- final Long curIfaceQuota = mInterfaceQuotas.get(iface);
-
- if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
-
- if (quotaBytes == QUOTA_UNLIMITED) {
- mInterfaceQuotas.remove(iface);
- } else {
- mInterfaceQuotas.put(iface, quotaBytes);
- }
- maybeUpdateDataLimit(iface);
- });
- }
-
- @VisibleForTesting
- void pushTetherStats() {
- try {
- // The token is not used for now. See b/153606961.
- notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats);
-
- // Clear the accumulated tether stats delta after reported. Note that create a new
- // empty object because NetworkStats#clear is @hide.
- mIfaceStats = new NetworkStats(0L, 0);
- mUidStats = new NetworkStats(0L, 0);
- } catch (RuntimeException e) {
- mLog.e("Cannot report network stats: ", e);
- }
- }
-
- private void accumulateDiff(@NonNull NetworkStats ifaceDiff,
- @NonNull NetworkStats uidDiff) {
- mIfaceStats = mIfaceStats.add(ifaceDiff);
- mUidStats = mUidStats.add(uidDiff);
- }
- }
-
- private boolean isBpfEnabled() {
- final TetheringConfiguration config = mDeps.getTetherConfig();
- return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */;
- }
-
- private int getInterfaceIndexFromRules(@NonNull String ifName) {
- for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
- .values()) {
- for (Ipv6ForwardingRule rule : rules.values()) {
- final int upstreamIfindex = rule.upstreamIfindex;
- if (TextUtils.equals(ifName, mInterfaceNames.get(upstreamIfindex))) {
- return upstreamIfindex;
- }
- }
- }
- return 0;
- }
-
- private long getQuotaBytes(@NonNull String iface) {
- final Long limit = mInterfaceQuotas.get(iface);
- final long quotaBytes = (limit != null) ? limit : QUOTA_UNLIMITED;
-
- return quotaBytes;
- }
-
- private boolean sendDataLimitToNetd(int ifIndex, long quotaBytes) {
- if (ifIndex == 0) {
- Log.wtf(TAG, "Invalid interface index.");
- return false;
- }
-
- try {
- mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Exception when updating quota " + quotaBytes + ": ", e);
- return false;
- }
-
- return true;
- }
-
- // Handle the data limit update from the service which is the stats provider registered for.
- private void maybeUpdateDataLimit(@NonNull String iface) {
- // Set data limit only on a given upstream which has at least one rule. If we can't get
- // an interface index for a given interface name, it means either there is no rule for
- // a given upstream or the interface name is not an upstream which is monitored by the
- // coordinator.
- final int ifIndex = getInterfaceIndexFromRules(iface);
- if (ifIndex == 0) return;
-
- final long quotaBytes = getQuotaBytes(iface);
- sendDataLimitToNetd(ifIndex, quotaBytes);
- }
-
- // Handle the data limit update while adding forwarding rules.
- private boolean updateDataLimit(int ifIndex) {
- final String iface = mInterfaceNames.get(ifIndex);
- if (iface == null) {
- mLog.e("Fail to get the interface name for index " + ifIndex);
- return false;
- }
- final long quotaBytes = getQuotaBytes(iface);
- return sendDataLimitToNetd(ifIndex, quotaBytes);
- }
-
- private boolean isAnyRuleOnUpstream(int upstreamIfindex) {
- for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
- .values()) {
- for (Ipv6ForwardingRule rule : rules.values()) {
- if (upstreamIfindex == rule.upstreamIfindex) return true;
- }
- }
- return false;
- }
-
- @NonNull
- private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex,
- @NonNull final ForwardedStats diff) {
- NetworkStats stats = new NetworkStats(0L, 0);
- final String iface = mInterfaceNames.get(ifIndex);
- if (iface == null) {
- // TODO: Use Log.wtf once the coordinator owns full control of tether stats from netd.
- // For now, netd may add the empty stats for the upstream which is not monitored by
- // the coordinator. Silently ignore it.
- return stats;
- }
- final int uid = (type == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
- // Note that the argument 'metered', 'roaming' and 'defaultNetwork' are not recorded for
- // network stats snapshot. See NetworkStatsRecorder#recordSnapshotLocked.
- return stats.addEntry(new Entry(iface, uid, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, DEFAULT_NETWORK_NO, diff.rxBytes, diff.rxPackets,
- diff.txBytes, diff.txPackets, 0L /* operations */));
- }
-
- private void updateAlertQuota(long newQuota) {
- if (newQuota < QUOTA_UNLIMITED) {
- throw new IllegalArgumentException("invalid quota value " + newQuota);
- }
- if (mRemainingAlertQuota == newQuota) return;
-
- mRemainingAlertQuota = newQuota;
- if (mRemainingAlertQuota == 0) {
- mLog.i("onAlertReached");
- if (mStatsProvider != null) mStatsProvider.notifyAlertReached();
- }
- }
-
- private void updateQuotaAndStatsFromSnapshot(
- @NonNull final TetherStatsParcel[] tetherStatsList) {
- long usedAlertQuota = 0;
- for (TetherStatsParcel tetherStats : tetherStatsList) {
- final Integer ifIndex = tetherStats.ifIndex;
- final ForwardedStats curr = new ForwardedStats(tetherStats);
- final ForwardedStats base = mStats.get(ifIndex);
- final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr;
- usedAlertQuota += diff.rxBytes + diff.txBytes;
-
- // Update the local cache for counting tether stats delta.
- mStats.put(ifIndex, curr);
-
- // Update the accumulated tether stats delta to the stats provider for the service
- // querying.
- if (mStatsProvider != null) {
- try {
- mStatsProvider.accumulateDiff(
- buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff),
- buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff));
- } catch (ArrayIndexOutOfBoundsException e) {
- Log.wtf(TAG, "Fail to update the accumulated stats delta for interface index "
- + ifIndex + " : ", e);
- }
- }
- }
-
- if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) {
- // Trim to zero if overshoot.
- final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0);
- updateAlertQuota(newQuota);
- }
-
- // TODO: Count the used limit quota for notifying data limit reached.
- }
-
- private void updateForwardedStatsFromNetd() {
- final TetherStatsParcel[] tetherStatsList;
- try {
- // The reported tether stats are total data usage for all currently-active upstream
- // interfaces since tethering start.
- tetherStatsList = mNetd.tetherOffloadGetStats();
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Problem fetching tethering stats: ", e);
- return;
- }
- updateQuotaAndStatsFromSnapshot(tetherStatsList);
- }
-
- @VisibleForTesting
- int getPollingInterval() {
- // The valid range of interval is DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long.
- // Ignore the config value is less than the minimum polling interval. Note that the
- // minimum interval definition is invoked as OffloadController#isPollingStatsNeeded does.
- // TODO: Perhaps define a minimum polling interval constant.
- final TetheringConfiguration config = mDeps.getTetherConfig();
- final int configInterval = (config != null) ? config.getOffloadPollInterval() : 0;
- return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval);
- }
-
- private void maybeSchedulePollingStats() {
- if (!mPollingStarted) return;
-
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
- }
-
- mHandler.postDelayed(mScheduledPollingTask, getPollingInterval());
- }
-
- // Return forwarding rule map. This is used for testing only.
- // Note that this can be only called on handler thread.
- @NonNull
- @VisibleForTesting
- final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
- getForwardingRulesForTesting() {
- return mIpv6ForwardingRules;
- }
-
- // Return upstream interface name map. This is used for testing only.
- // Note that this can be only called on handler thread.
- @NonNull
- @VisibleForTesting
- final SparseArray<String> getInterfaceNamesForTesting() {
- return mInterfaceNames;
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java b/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
deleted file mode 100644
index 8a96988ae1d1..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
+++ /dev/null
@@ -1,183 +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.networkstack.tethering;
-
-import static android.net.TetheringManager.TETHERING_WIFI;
-
-import android.net.MacAddress;
-import android.net.TetheredClient;
-import android.net.TetheredClient.AddressInfo;
-import android.net.ip.IpServer;
-import android.net.wifi.WifiClient;
-import android.os.SystemClock;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Tracker for clients connected to downstreams.
- *
- * <p>This class is not thread safe, it is intended to be used only from the tethering handler
- * thread.
- */
-public class ConnectedClientsTracker {
- private final Clock mClock;
-
- @NonNull
- private List<WifiClient> mLastWifiClients = Collections.emptyList();
- @NonNull
- private List<TetheredClient> mLastTetheredClients = Collections.emptyList();
-
- @VisibleForTesting
- static class Clock {
- public long elapsedRealtime() {
- return SystemClock.elapsedRealtime();
- }
- }
-
- public ConnectedClientsTracker() {
- this(new Clock());
- }
-
- @VisibleForTesting
- ConnectedClientsTracker(Clock clock) {
- mClock = clock;
- }
-
- /**
- * Update the tracker with new connected clients.
- *
- * <p>The new list can be obtained through {@link #getLastTetheredClients()}.
- * @param ipServers The IpServers used to assign addresses to clients.
- * @param wifiClients The list of L2-connected WiFi clients. Null for no change since last
- * update.
- * @return True if the list of clients changed since the last calculation.
- */
- public boolean updateConnectedClients(
- Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients) {
- final long now = mClock.elapsedRealtime();
-
- if (wifiClients != null) {
- mLastWifiClients = wifiClients;
- }
- final Set<MacAddress> wifiClientMacs = getClientMacs(mLastWifiClients);
-
- // Build the list of non-expired leases from all IpServers, grouped by mac address
- final Map<MacAddress, TetheredClient> clientsMap = new HashMap<>();
- for (IpServer server : ipServers) {
- for (TetheredClient client : server.getAllLeases()) {
- if (client.getTetheringType() == TETHERING_WIFI
- && !wifiClientMacs.contains(client.getMacAddress())) {
- // Skip leases of WiFi clients that are not (or no longer) L2-connected
- continue;
- }
- final TetheredClient prunedClient = pruneExpired(client, now);
- if (prunedClient == null) continue; // All addresses expired
-
- addLease(clientsMap, prunedClient);
- }
- }
-
- // TODO: add IPv6 addresses from netlink
-
- // Add connected WiFi clients that do not have any known address
- for (MacAddress client : wifiClientMacs) {
- if (clientsMap.containsKey(client)) continue;
- clientsMap.put(client, new TetheredClient(
- client, Collections.emptyList() /* addresses */, TETHERING_WIFI));
- }
-
- final HashSet<TetheredClient> clients = new HashSet<>(clientsMap.values());
- final boolean clientsChanged = clients.size() != mLastTetheredClients.size()
- || !clients.containsAll(mLastTetheredClients);
- mLastTetheredClients = Collections.unmodifiableList(new ArrayList<>(clients));
- return clientsChanged;
- }
-
- private static void addLease(Map<MacAddress, TetheredClient> clientsMap, TetheredClient lease) {
- final TetheredClient aggregateClient = clientsMap.getOrDefault(
- lease.getMacAddress(), lease);
- if (aggregateClient == lease) {
- // This is the first lease with this mac address
- clientsMap.put(lease.getMacAddress(), lease);
- return;
- }
-
- // Only add the address info; this assumes that the tethering type is the same when the mac
- // address is the same. If a client is connected through different tethering types with the
- // same mac address, connected clients callbacks will report all of its addresses under only
- // one of these tethering types. This keeps the API simple considering that such a scenario
- // would really be a rare edge case.
- clientsMap.put(lease.getMacAddress(), aggregateClient.addAddresses(lease));
- }
-
- /**
- * Get the last list of tethered clients, as calculated in {@link #updateConnectedClients}.
- *
- * <p>The returned list is immutable.
- */
- @NonNull
- public List<TetheredClient> getLastTetheredClients() {
- return mLastTetheredClients;
- }
-
- private static boolean hasExpiredAddress(List<AddressInfo> addresses, long now) {
- for (AddressInfo info : addresses) {
- if (info.getExpirationTime() <= now) {
- return true;
- }
- }
- return false;
- }
-
- @Nullable
- private static TetheredClient pruneExpired(TetheredClient client, long now) {
- final List<AddressInfo> addresses = client.getAddresses();
- if (addresses.size() == 0) return null;
- if (!hasExpiredAddress(addresses, now)) return client;
-
- final ArrayList<AddressInfo> newAddrs = new ArrayList<>(addresses.size() - 1);
- for (AddressInfo info : addresses) {
- if (info.getExpirationTime() > now) {
- newAddrs.add(info);
- }
- }
-
- if (newAddrs.size() == 0) {
- return null;
- }
- return new TetheredClient(client.getMacAddress(), newAddrs, client.getTetheringType());
- }
-
- @NonNull
- private static Set<MacAddress> getClientMacs(@NonNull List<WifiClient> clients) {
- final Set<MacAddress> macs = new HashSet<>(clients.size());
- for (WifiClient c : clients) {
- macs.add(c.getMacAddress());
- }
- return macs;
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
deleted file mode 100644
index bb7322f2a0d2..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
-import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
-import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
-import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
-import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
-import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
-import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
-import static android.net.TetheringManager.TETHERING_BLUETOOTH;
-import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringManager.TETHERING_INVALID;
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.util.SharedLog;
-import android.os.Bundle;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.PersistableBundle;
-import android.os.ResultReceiver;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
-import android.util.SparseIntArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.util.BitSet;
-
-/**
- * Re-check tethering provisioning for enabled downstream tether types.
- * Reference TetheringManager.TETHERING_{@code *} for each tether type.
- *
- * All methods of this class must be accessed from the thread of tethering
- * state machine.
- * @hide
- */
-public class EntitlementManager {
- private static final String TAG = EntitlementManager.class.getSimpleName();
- private static final boolean DBG = false;
-
- @VisibleForTesting
- protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
- private static final String ACTION_PROVISIONING_ALARM =
- "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM";
-
- private final ComponentName mSilentProvisioningService;
- private static final int MS_PER_HOUR = 60 * 60 * 1000;
- private static final int DUMP_TIMEOUT = 10_000;
-
- // The BitSet is the bit map of each enabled downstream types, ex:
- // {@link TetheringManager.TETHERING_WIFI}
- // {@link TetheringManager.TETHERING_USB}
- // {@link TetheringManager.TETHERING_BLUETOOTH}
- private final BitSet mCurrentDownstreams;
- private final BitSet mExemptedDownstreams;
- private final Context mContext;
- private final SharedLog mLog;
- private final SparseIntArray mEntitlementCacheValue;
- private final Handler mHandler;
- // Key: TetheringManager.TETHERING_*(downstream).
- // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
- private final SparseIntArray mCurrentEntitlementResults;
- private final Runnable mPermissionChangeCallback;
- private PendingIntent mProvisioningRecheckAlarm;
- private boolean mLastCellularUpstreamPermitted = true;
- private boolean mUsingCellularAsUpstream = false;
- private boolean mNeedReRunProvisioningUi = false;
- private OnUiEntitlementFailedListener mListener;
- private TetheringConfigurationFetcher mFetcher;
-
- public EntitlementManager(Context ctx, Handler h, SharedLog log,
- Runnable callback) {
- mContext = ctx;
- mLog = log.forSubComponent(TAG);
- mCurrentDownstreams = new BitSet();
- mExemptedDownstreams = new BitSet();
- mCurrentEntitlementResults = new SparseIntArray();
- mEntitlementCacheValue = new SparseIntArray();
- mPermissionChangeCallback = callback;
- mHandler = h;
- mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
- null, mHandler);
- mSilentProvisioningService = ComponentName.unflattenFromString(
- mContext.getResources().getString(R.string.config_wifi_tether_enable));
- }
-
- public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) {
- mListener = listener;
- }
-
- /** Callback fired when UI entitlement failed. */
- public interface OnUiEntitlementFailedListener {
- /**
- * Ui entitlement check fails in |downstream|.
- *
- * @param downstream tethering type from TetheringManager.TETHERING_{@code *}.
- */
- void onUiEntitlementFailed(int downstream);
- }
-
- public void setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher) {
- mFetcher = fetcher;
- }
-
- /** Interface to fetch TetheringConfiguration. */
- public interface TetheringConfigurationFetcher {
- /**
- * Fetch current tethering configuration. This will be called to ensure whether entitlement
- * check is needed.
- * @return TetheringConfiguration instance.
- */
- TetheringConfiguration fetchTetheringConfiguration();
- }
-
- /**
- * Check if cellular upstream is permitted.
- */
- public boolean isCellularUpstreamPermitted() {
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-
- return isCellularUpstreamPermitted(config);
- }
-
- private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) {
- if (!isTetherProvisioningRequired(config)) return true;
-
- // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular
- // upstream should not be enabled. Enable cellular upstream for exempted downstreams only
- // when there is no non-exempted downstream.
- if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty();
-
- return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1;
- }
-
- /**
- * Set exempted downstream type. If there is only exempted downstream type active,
- * corresponding entitlement check will not be run and cellular upstream will be permitted
- * by default. If a privileged app enables tethering without a provisioning check, and then
- * another app enables tethering of the same type but does not disable the provisioning check,
- * then the downstream immediately loses exempt status and a provisioning check is run.
- * If any non-exempted downstream type is active, the cellular upstream will be gated by the
- * result of entitlement check from non-exempted downstreams. If entitlement check is still
- * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any
- * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled.
- */
- public void setExemptedDownstreamType(final int type) {
- mExemptedDownstreams.set(type, true);
- }
-
- /**
- * This is called when tethering starts.
- * Launch provisioning app if upstream is cellular.
- *
- * @param downstreamType tethering type from TetheringManager.TETHERING_{@code *}
- * @param showProvisioningUi a boolean indicating whether to show the
- * provisioning app UI if there is one.
- */
- public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
- if (!isValidDownstreamType(downstreamType)) return;
-
- mCurrentDownstreams.set(downstreamType, true);
-
- mExemptedDownstreams.set(downstreamType, false);
-
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- if (!isTetherProvisioningRequired(config)) return;
-
- // If upstream is not cellular, provisioning app would not be launched
- // till upstream change to cellular.
- if (mUsingCellularAsUpstream) {
- if (showProvisioningUi) {
- runUiTetherProvisioning(downstreamType, config);
- } else {
- runSilentTetherProvisioning(downstreamType, config);
- }
- mNeedReRunProvisioningUi = false;
- } else {
- mNeedReRunProvisioningUi |= showProvisioningUi;
- }
- }
-
- /**
- * Tell EntitlementManager that a given type of tethering has been disabled
- *
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- */
- public void stopProvisioningIfNeeded(int downstreamType) {
- if (!isValidDownstreamType(downstreamType)) return;
-
- mCurrentDownstreams.set(downstreamType, false);
- // There are lurking bugs where the notion of "provisioning required" or
- // "tethering supported" may change without without tethering being notified properly.
- // Remove the mapping all the time no matter provisioning is required or not.
- removeDownstreamMapping(downstreamType);
- mExemptedDownstreams.set(downstreamType, false);
- }
-
- /**
- * Notify EntitlementManager if upstream is cellular or not.
- *
- * @param isCellular whether tethering upstream is cellular.
- */
- public void notifyUpstream(boolean isCellular) {
- if (DBG) {
- mLog.i("notifyUpstream: " + isCellular
- + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted
- + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi);
- }
- mUsingCellularAsUpstream = isCellular;
-
- if (mUsingCellularAsUpstream) {
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- maybeRunProvisioning(config);
- }
- }
-
- /** Run provisioning if needed */
- public void maybeRunProvisioning() {
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- maybeRunProvisioning(config);
- }
-
- private void maybeRunProvisioning(final TetheringConfiguration config) {
- if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) {
- return;
- }
-
- // Whenever any entitlement value changes, all downstreams will re-evaluate whether they
- // are allowed. Therefore even if the silent check here ends in a failure and the UI later
- // yields success, then the downstream that got a failure will re-evaluate as a result of
- // the change and get the new correct value.
- for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0;
- downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) {
- if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
- if (mNeedReRunProvisioningUi) {
- mNeedReRunProvisioningUi = false;
- runUiTetherProvisioning(downstream, config);
- } else {
- runSilentTetherProvisioning(downstream, config);
- }
- }
- }
- }
-
- /**
- * Check if the device requires a provisioning check in order to enable tethering.
- *
- * @param config an object that encapsulates the various tethering configuration elements.
- * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
- */
- @VisibleForTesting
- protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) {
- if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
- || config.provisioningApp.length == 0) {
- return false;
- }
- if (carrierConfigAffirmsEntitlementCheckNotRequired(config)) {
- return false;
- }
- return (config.provisioningApp.length == 2);
- }
-
- /**
- * Re-check tethering provisioning for all enabled tether types.
- * Reference TetheringManager.TETHERING_{@code *} for each tether type.
- *
- * @param config an object that encapsulates the various tethering configuration elements.
- * Note: this method is only called from @{link Tethering.TetherMainSM} on the handler thread.
- * If there are new callers from different threads, the logic should move to
- * @{link Tethering.TetherMainSM} handler to avoid race conditions.
- */
- public void reevaluateSimCardProvisioning(final TetheringConfiguration config) {
- if (DBG) mLog.i("reevaluateSimCardProvisioning");
-
- if (!mHandler.getLooper().isCurrentThread()) {
- // Except for test, this log should not appear in normal flow.
- mLog.log("reevaluateSimCardProvisioning() don't run in TetherMainSM thread");
- }
- mEntitlementCacheValue.clear();
- mCurrentEntitlementResults.clear();
-
- // TODO: refine provisioning check to isTetherProvisioningRequired() ??
- if (!config.hasMobileHotspotProvisionApp()
- || carrierConfigAffirmsEntitlementCheckNotRequired(config)) {
- evaluateCellularPermission(config);
- return;
- }
-
- if (mUsingCellularAsUpstream) {
- maybeRunProvisioning(config);
- }
- }
-
- /**
- * Get carrier configuration bundle.
- * @param config an object that encapsulates the various tethering configuration elements.
- * */
- public PersistableBundle getCarrierConfig(final TetheringConfiguration config) {
- final CarrierConfigManager configManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) return null;
-
- final PersistableBundle carrierConfig = configManager.getConfigForSubId(
- config.activeDataSubId);
-
- if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
- return carrierConfig;
- }
-
- return null;
- }
-
- // The logic here is aimed solely at confirming that a CarrierConfig exists
- // and affirms that entitlement checks are not required.
- //
- // TODO: find a better way to express this, or alter the checking process
- // entirely so that this is more intuitive.
- private boolean carrierConfigAffirmsEntitlementCheckNotRequired(
- final TetheringConfiguration config) {
- // Check carrier config for entitlement checks
- final PersistableBundle carrierConfig = getCarrierConfig(config);
- if (carrierConfig == null) return false;
-
- // A CarrierConfigManager was found and it has a config.
- final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
- CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
- return !isEntitlementCheckRequired;
- }
-
- /**
- * Run no UI tethering provisioning check.
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- * @param subId default data subscription ID.
- */
- @VisibleForTesting
- protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) {
- if (DBG) mLog.i("runSilentTetherProvisioning: " + type);
- // For silent provisioning, settings would stop tethering when entitlement fail.
- ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null);
-
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_RUN_PROVISION, true);
- intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi);
- intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
- intent.setComponent(mSilentProvisioningService);
- // Only admin user can change tethering and SilentTetherProvisioning don't need to
- // show UI, it is fine to always start setting's background service as system user.
- mContext.startService(intent);
- return intent;
- }
-
- private void runUiTetherProvisioning(int type, final TetheringConfiguration config) {
- ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null);
- runUiTetherProvisioning(type, config, receiver);
- }
-
- /**
- * Run the UI-enabled tethering provisioning check.
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- * @param subId default data subscription ID.
- * @param receiver to receive entitlement check result.
- */
- @VisibleForTesting
- protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config,
- ResultReceiver receiver) {
- if (DBG) mLog.i("runUiTetherProvisioning: " + type);
-
- Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI);
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // Only launch entitlement UI for system user. Entitlement UI should not appear for other
- // user because only admin user is allowed to change tethering.
- mContext.startActivity(intent);
- return intent;
- }
-
- // Not needed to check if this don't run on the handler thread because it's private.
- private void scheduleProvisioningRechecks(final TetheringConfiguration config) {
- if (mProvisioningRecheckAlarm == null) {
- final int period = config.provisioningCheckPeriod;
- if (period <= 0) return;
-
- Intent intent = new Intent(ACTION_PROVISIONING_ALARM);
- mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
- Context.ALARM_SERVICE);
- long periodMs = period * MS_PER_HOUR;
- long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs;
- alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs,
- mProvisioningRecheckAlarm);
- }
- }
-
- private void cancelTetherProvisioningRechecks() {
- if (mProvisioningRecheckAlarm != null) {
- AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
- Context.ALARM_SERVICE);
- alarmManager.cancel(mProvisioningRecheckAlarm);
- mProvisioningRecheckAlarm = null;
- }
- }
-
- private void evaluateCellularPermission(final TetheringConfiguration config) {
- final boolean permitted = isCellularUpstreamPermitted(config);
-
- if (DBG) {
- mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted
- + " to " + permitted);
- }
-
- if (mLastCellularUpstreamPermitted != permitted) {
- mLog.log("Cellular permission change: " + permitted);
- mPermissionChangeCallback.run();
- }
- // Only schedule periodic re-check when tether is provisioned
- // and the result is ok.
- if (permitted && mCurrentEntitlementResults.size() > 0) {
- scheduleProvisioningRechecks(config);
- } else {
- cancelTetherProvisioningRechecks();
- }
- mLastCellularUpstreamPermitted = permitted;
- }
-
- /**
- * Add the mapping between provisioning result and tethering type.
- * Notify UpstreamNetworkMonitor if Cellular permission changes.
- *
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- * @param resultCode Provisioning result
- */
- protected void addDownstreamMapping(int type, int resultCode) {
- mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode
- + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type));
- if (!mCurrentDownstreams.get(type)) return;
-
- mCurrentEntitlementResults.put(type, resultCode);
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- evaluateCellularPermission(config);
- }
-
- /**
- * Remove the mapping for input tethering type.
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- */
- protected void removeDownstreamMapping(int type) {
- mLog.i("removeDownstreamMapping: " + type);
- mCurrentEntitlementResults.delete(type);
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- evaluateCellularPermission(config);
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) {
- mLog.log("Received provisioning alarm");
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- reevaluateSimCardProvisioning(config);
- }
- }
- };
-
- private static boolean isValidDownstreamType(int type) {
- switch (type) {
- case TETHERING_BLUETOOTH:
- case TETHERING_ETHERNET:
- case TETHERING_USB:
- case TETHERING_WIFI:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Dump the infromation of EntitlementManager.
- * @param pw {@link PrintWriter} is used to print formatted
- */
- public void dump(PrintWriter pw) {
- final ConditionVariable mWaiting = new ConditionVariable();
- mHandler.post(() -> {
- pw.print("isCellularUpstreamPermitted: ");
- pw.println(isCellularUpstreamPermitted());
- for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0;
- type = mCurrentDownstreams.nextSetBit(type + 1)) {
- pw.print("Type: ");
- pw.print(typeString(type));
- if (mCurrentEntitlementResults.indexOfKey(type) > -1) {
- pw.print(", Value: ");
- pw.println(errorString(mCurrentEntitlementResults.get(type)));
- } else {
- pw.println(", Value: empty");
- }
- }
- mWaiting.open();
- });
- if (!mWaiting.block(DUMP_TIMEOUT)) {
- pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms");
- }
- pw.print("Exempted: [");
- for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0;
- type = mExemptedDownstreams.nextSetBit(type + 1)) {
- pw.print(typeString(type));
- pw.print(", ");
- }
- pw.println("]");
- }
-
- private static String typeString(int type) {
- switch (type) {
- case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH";
- case TETHERING_INVALID: return "TETHERING_INVALID";
- case TETHERING_USB: return "TETHERING_USB";
- case TETHERING_WIFI: return "TETHERING_WIFI";
- default:
- return String.format("TETHERING UNKNOWN TYPE (%d)", type);
- }
- }
-
- private static String errorString(int value) {
- switch (value) {
- case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
- case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR";
- case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED";
- default:
- return String.format("UNKNOWN ERROR (%d)", value);
- }
- }
-
- private ResultReceiver buildProxyReceiver(int type, boolean notifyFail,
- final ResultReceiver receiver) {
- ResultReceiver rr = new ResultReceiver(mHandler) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
- addDownstreamMapping(type, updatedCacheValue);
- if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) {
- mListener.onUiEntitlementFailed(type);
- }
- if (receiver != null) receiver.send(updatedCacheValue, null);
- }
- };
-
- return writeToParcel(rr);
- }
-
- // Instances of ResultReceiver need to be public classes for remote processes to be able
- // to load them (otherwise, ClassNotFoundException). For private classes, this method
- // performs a trick : round-trip parceling any instance of ResultReceiver will return a
- // vanilla instance of ResultReceiver sharing the binder token with the original receiver.
- // The binder token has a reference to the original instance of the private class and will
- // still call its methods, and can be sent over. However it cannot be used for anything
- // else than sending over a Binder call.
- // While round-trip parceling is not great, there is currently no other way of generating
- // a vanilla instance of ResultReceiver because all its fields are private.
- private ResultReceiver writeToParcel(final ResultReceiver receiver) {
- Parcel parcel = Parcel.obtain();
- receiver.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
- parcel.recycle();
- return receiverForSending;
- }
-
- /**
- * Update the last entitlement value to internal cache
- *
- * @param type tethering type from TetheringManager.TETHERING_{@code *}
- * @param resultCode last entitlement value
- * @return the last updated entitlement value
- */
- private int updateEntitlementCacheValue(int type, int resultCode) {
- if (DBG) {
- mLog.i("updateEntitlementCacheValue: " + type + ", result: " + resultCode);
- }
- if (resultCode == TETHER_ERROR_NO_ERROR) {
- mEntitlementCacheValue.put(type, resultCode);
- return resultCode;
- } else {
- mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED);
- return TETHER_ERROR_PROVISIONING_FAILED;
- }
- }
-
- /** Get the last value of the tethering entitlement check. */
- public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
- boolean showEntitlementUi) {
- if (!isValidDownstreamType(downstream)) {
- receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null);
- return;
- }
-
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- if (!isTetherProvisioningRequired(config)) {
- receiver.send(TETHER_ERROR_NO_ERROR, null);
- return;
- }
-
- final int cacheValue = mEntitlementCacheValue.get(
- downstream, TETHER_ERROR_ENTITLEMENT_UNKNOWN);
- if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
- receiver.send(cacheValue, null);
- } else {
- ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver);
- runUiTetherProvisioning(downstream, config, proxy);
- }
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
deleted file mode 100644
index f3dcaa2529e7..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.RouteInfo;
-import android.net.ip.IpServer;
-import android.net.util.NetworkConstants;
-import android.net.util.SharedLog;
-import android.util.Log;
-
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.Random;
-
-
-/**
- * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
- * This coordinator is responsible for evaluating the dedicated prefixes
- * assigned to the device and deciding how to divvy them up among downstream
- * interfaces.
- *
- * @hide
- */
-public class IPv6TetheringCoordinator {
- private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- private static class Downstream {
- public final IpServer ipServer;
- public final int mode; // IpServer.STATE_*
- // Used to append to a ULA /48, constructing a ULA /64 for local use.
- public final short subnetId;
-
- Downstream(IpServer ipServer, int mode, short subnetId) {
- this.ipServer = ipServer;
- this.mode = mode;
- this.subnetId = subnetId;
- }
- }
-
- private final ArrayList<IpServer> mNotifyList;
- private final SharedLog mLog;
- // NOTE: mActiveDownstreams is a list and not a hash data structure because
- // we keep active downstreams in arrival order. This is done so /64s can
- // be parceled out on a "first come, first served" basis and a /64 used by
- // a downstream that is no longer active can be redistributed to any next
- // waiting active downstream (again, in arrival order).
- private final LinkedList<Downstream> mActiveDownstreams;
- private final byte[] mUniqueLocalPrefix;
- private short mNextSubnetId;
- private UpstreamNetworkState mUpstreamNetworkState;
-
- public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
- mNotifyList = notifyList;
- mLog = log.forSubComponent(TAG);
- mActiveDownstreams = new LinkedList<>();
- mUniqueLocalPrefix = generateUniqueLocalPrefix();
- mNextSubnetId = 0;
- }
-
- /** Add active downstream to ipv6 tethering candidate list. */
- public void addActiveDownstream(IpServer downstream, int mode) {
- if (findDownstream(downstream) == null) {
- // Adding a new downstream appends it to the list. Adding a
- // downstream a second time without first removing it has no effect.
- // We never change the mode of a downstream except by first removing
- // it and then re-adding it (with its new mode specified);
- if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) {
- // Make sure subnet IDs are always positive. They are appended
- // to a ULA /48 to make a ULA /64 for local use.
- mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1);
- }
- updateIPv6TetheringInterfaces();
- }
- }
-
- /** Remove downstream from ipv6 tethering candidate list. */
- public void removeActiveDownstream(IpServer downstream) {
- stopIPv6TetheringOn(downstream);
- if (mActiveDownstreams.remove(findDownstream(downstream))) {
- updateIPv6TetheringInterfaces();
- }
-
- // When tethering is stopping we can reset the subnet counter.
- if (mNotifyList.isEmpty()) {
- if (!mActiveDownstreams.isEmpty()) {
- Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty.");
- }
- mNextSubnetId = 0;
- }
- }
-
- /**
- * Call when UpstreamNetworkState may be changed.
- * If upstream has ipv6 for tethering, update this new UpstreamNetworkState
- * to IpServer. Otherwise stop ipv6 tethering on downstream interfaces.
- */
- public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
- if (VDBG) {
- Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
- }
- if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
- stopIPv6TetheringOnAllInterfaces();
- setUpstreamNetworkState(null);
- return;
- }
-
- if (mUpstreamNetworkState != null
- && !ns.network.equals(mUpstreamNetworkState.network)) {
- stopIPv6TetheringOnAllInterfaces();
- }
-
- setUpstreamNetworkState(ns);
- updateIPv6TetheringInterfaces();
- }
-
- private void stopIPv6TetheringOnAllInterfaces() {
- for (IpServer ipServer : mNotifyList) {
- stopIPv6TetheringOn(ipServer);
- }
- }
-
- private void setUpstreamNetworkState(UpstreamNetworkState ns) {
- if (ns == null) {
- mUpstreamNetworkState = null;
- } else {
- // Make a deep copy of the parts we need.
- mUpstreamNetworkState = new UpstreamNetworkState(
- new LinkProperties(ns.linkProperties),
- new NetworkCapabilities(ns.networkCapabilities),
- new Network(ns.network));
- }
-
- mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
- }
-
- private void updateIPv6TetheringInterfaces() {
- for (IpServer ipServer : mNotifyList) {
- final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
- ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp);
- break;
- }
- }
-
- private int getTtlAdjustment() {
- if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) {
- return 0;
- }
-
- // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1
- // for carrier requirement.
- if (mUpstreamNetworkState.networkCapabilities.hasTransport(
- NetworkCapabilities.TRANSPORT_CELLULAR)) {
- return -1;
- }
-
- // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary
- // distinction between tethered and untethered traffic.
- return 1;
- }
-
- private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
- final Downstream ds = findDownstream(ipServer);
- if (ds == null) return null;
-
- if (ds.mode == IpServer.STATE_LOCAL_ONLY) {
- // Build a Unique Locally-assigned Prefix configuration.
- return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
- }
-
- // This downstream is in IpServer.STATE_TETHERED mode.
- if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
- return null;
- }
-
- // NOTE: Here, in future, we would have policies to decide how to divvy
- // up the available dedicated prefixes among downstream interfaces.
- // At this time we have no such mechanism--we only support tethering
- // IPv6 toward the oldest (first requested) active downstream.
-
- final Downstream currentActive = mActiveDownstreams.peek();
- if (currentActive != null && currentActive.ipServer == ipServer) {
- final LinkProperties lp = getIPv6OnlyLinkProperties(
- mUpstreamNetworkState.linkProperties);
- if (lp.hasIpv6DefaultRoute() && lp.hasGlobalIpv6Address()) {
- return lp;
- }
- }
-
- return null;
- }
-
- Downstream findDownstream(IpServer ipServer) {
- for (Downstream ds : mActiveDownstreams) {
- if (ds.ipServer == ipServer) return ds;
- }
- return null;
- }
-
- private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
- final LinkProperties v6only = new LinkProperties();
- if (lp == null) {
- return v6only;
- }
-
- // NOTE: At this time we don't copy over any information about any
- // stacked links. No current stacked link configuration has IPv6.
-
- v6only.setInterfaceName(lp.getInterfaceName());
-
- v6only.setMtu(lp.getMtu());
-
- for (LinkAddress linkAddr : lp.getLinkAddresses()) {
- if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
- v6only.addLinkAddress(linkAddr);
- }
- }
-
- for (RouteInfo routeInfo : lp.getRoutes()) {
- final IpPrefix destination = routeInfo.getDestination();
- if ((destination.getAddress() instanceof Inet6Address)
- && (destination.getPrefixLength() <= 64)) {
- v6only.addRoute(routeInfo);
- }
- }
-
- for (InetAddress dnsServer : lp.getDnsServers()) {
- if (isIPv6GlobalAddress(dnsServer)) {
- // For now we include ULAs.
- v6only.addDnsServer(dnsServer);
- }
- }
-
- v6only.setDomains(lp.getDomains());
-
- return v6only;
- }
-
- // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
- // announce our own IPv6 address as DNS server.
- private static boolean isIPv6GlobalAddress(InetAddress ip) {
- return (ip instanceof Inet6Address)
- && !ip.isAnyLocalAddress()
- && !ip.isLoopbackAddress()
- && !ip.isLinkLocalAddress()
- && !ip.isSiteLocalAddress()
- && !ip.isMulticastAddress();
- }
-
- private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) {
- final LinkProperties lp = new LinkProperties();
-
- final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48);
- lp.addRoute(new RouteInfo(local48, null, null, RouteInfo.RTN_UNICAST));
-
- final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64);
- // Because this is a locally-generated ULA, we don't have an upstream
- // address. But because the downstream IP address management code gets
- // its prefix from the upstream's IP address, we create a fake one here.
- lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64));
-
- lp.setMtu(NetworkConstants.ETHER_MTU);
- return lp;
- }
-
- private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) {
- final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length);
- bytes[7] = (byte) (subnetId >> 8);
- bytes[8] = (byte) subnetId;
- final InetAddress addr;
- try {
- addr = InetAddress.getByAddress(bytes);
- } catch (UnknownHostException e) {
- throw new IllegalStateException("Invalid address length: " + bytes.length, e);
- }
- return new IpPrefix(addr, prefixlen);
- }
-
- // Generates a Unique Locally-assigned Prefix:
- //
- // https://tools.ietf.org/html/rfc4193#section-3.1
- //
- // The result is a /48 that can be used for local-only communications.
- private static byte[] generateUniqueLocalPrefix() {
- final byte[] ulp = new byte[6]; // 6 = 48bits / 8bits/byte
- (new Random()).nextBytes(ulp);
-
- final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN);
- in6addr[0] = (byte) 0xfd; // fc00::/7 and L=1
-
- return in6addr;
- }
-
- private static String toDebugString(UpstreamNetworkState ns) {
- if (ns == null) {
- return "UpstreamNetworkState{null}";
- }
- return ns.toString();
- }
-
- private static void stopIPv6TetheringOn(IpServer ipServer) {
- ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
deleted file mode 100644
index 88c77b07e7e3..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ /dev/null
@@ -1,811 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStats.UID_TETHERING;
-import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
-import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
-
-import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.usage.NetworkStatsManager;
-import android.content.ContentResolver;
-import android.net.InetAddresses;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NetworkStats;
-import android.net.NetworkStats.Entry;
-import android.net.RouteInfo;
-import android.net.netlink.ConntrackMessage;
-import android.net.netlink.NetlinkConstants;
-import android.net.netlink.NetlinkSocket;
-import android.net.netstats.provider.NetworkStatsProvider;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.provider.Settings;
-import android.system.ErrnoException;
-import android.system.OsConstants;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
-
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * A class to encapsulate the business logic of programming the tethering
- * hardware offload interface.
- *
- * @hide
- */
-public class OffloadController {
- private static final String TAG = OffloadController.class.getSimpleName();
- private static final boolean DBG = false;
- private static final String ANYIP = "0.0.0.0";
- private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
-
- @VisibleForTesting
- enum StatsType {
- STATS_PER_IFACE,
- STATS_PER_UID,
- }
-
- private enum UpdateType { IF_NEEDED, FORCE };
-
- private final Handler mHandler;
- private final OffloadHardwareInterface mHwInterface;
- private final ContentResolver mContentResolver;
- @Nullable
- private final OffloadTetheringStatsProvider mStatsProvider;
- private final SharedLog mLog;
- private final HashMap<String, LinkProperties> mDownstreams;
- private boolean mConfigInitialized;
- private boolean mControlInitialized;
- private LinkProperties mUpstreamLinkProperties;
- // The complete set of offload-exempt prefixes passed in via Tethering from
- // all upstream and downstream sources.
- private Set<IpPrefix> mExemptPrefixes;
- // A strictly "smaller" set of prefixes, wherein offload-approved prefixes
- // (e.g. downstream on-link prefixes) have been removed and replaced with
- // prefixes representing only the locally-assigned IP addresses.
- private Set<String> mLastLocalPrefixStrs;
-
- // Maps upstream interface names to offloaded traffic statistics.
- // Always contains the latest value received from the hardware for each interface, regardless of
- // whether offload is currently running on that interface.
- private ConcurrentHashMap<String, ForwardedStats> mForwardedStats =
- new ConcurrentHashMap<>(16, 0.75F, 1);
-
- // Maps upstream interface names to interface quotas.
- // Always contains the latest value received from the framework for each interface, regardless
- // of whether offload is currently running (or is even supported) on that interface. Only
- // includes upstream interfaces that have a quota set.
- private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
-
- // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
- // quota is interface independent and global for tether offload. Note that this is only
- // accessed on the handler thread and in the constructor.
- private long mRemainingAlertQuota = QUOTA_UNLIMITED;
- // Runnable that used to schedule the next stats poll.
- private final Runnable mScheduledPollingTask = () -> {
- updateStatsForCurrentUpstream();
- maybeSchedulePollingStats();
- };
-
- private int mNatUpdateCallbacksReceived;
- private int mNatUpdateNetlinkErrors;
-
- @NonNull
- private final Dependencies mDeps;
-
- // TODO: Put more parameters in constructor into dependency object.
- interface Dependencies {
- @NonNull
- TetheringConfiguration getTetherConfig();
- }
-
- public OffloadController(Handler h, OffloadHardwareInterface hwi,
- ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log,
- @NonNull Dependencies deps) {
- mHandler = h;
- mHwInterface = hwi;
- mContentResolver = contentResolver;
- mLog = log.forSubComponent(TAG);
- mDownstreams = new HashMap<>();
- mExemptPrefixes = new HashSet<>();
- mLastLocalPrefixStrs = new HashSet<>();
- OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider();
- try {
- nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Cannot register offload stats provider: " + e);
- provider = null;
- }
- mStatsProvider = provider;
- mDeps = deps;
- }
-
- /** Start hardware offload. */
- public boolean start() {
- if (started()) return true;
-
- if (isOffloadDisabled()) {
- mLog.i("tethering offload disabled");
- return false;
- }
-
- if (!mConfigInitialized) {
- mConfigInitialized = mHwInterface.initOffloadConfig();
- if (!mConfigInitialized) {
- mLog.i("tethering offload config not supported");
- stop();
- return false;
- }
- }
-
- mControlInitialized = mHwInterface.initOffloadControl(
- // OffloadHardwareInterface guarantees that these callback
- // methods are called on the handler passed to it, which is the
- // same as mHandler, as coordinated by the setup in Tethering.
- new OffloadHardwareInterface.ControlCallback() {
- @Override
- public void onStarted() {
- if (!started()) return;
- mLog.log("onStarted");
- }
-
- @Override
- public void onStoppedError() {
- if (!started()) return;
- mLog.log("onStoppedError");
- }
-
- @Override
- public void onStoppedUnsupported() {
- if (!started()) return;
- mLog.log("onStoppedUnsupported");
- // Poll for statistics and trigger a sweep of tethering
- // stats by observers. This might not succeed, but it's
- // worth trying anyway. We need to do this because from
- // this point on we continue with software forwarding,
- // and we need to synchronize stats and limits between
- // software and hardware forwarding.
- updateStatsForAllUpstreams();
- if (mStatsProvider != null) mStatsProvider.pushTetherStats();
- }
-
- @Override
- public void onSupportAvailable() {
- if (!started()) return;
- mLog.log("onSupportAvailable");
-
- // [1] Poll for statistics and trigger a sweep of stats
- // by observers. We need to do this to ensure that any
- // limits set take into account any software tethering
- // traffic that has been happening in the meantime.
- updateStatsForAllUpstreams();
- if (mStatsProvider != null) mStatsProvider.pushTetherStats();
- // [2] (Re)Push all state.
- computeAndPushLocalPrefixes(UpdateType.FORCE);
- pushAllDownstreamState();
- pushUpstreamParameters(null);
- }
-
- @Override
- public void onStoppedLimitReached() {
- if (!started()) return;
- mLog.log("onStoppedLimitReached");
-
- // We cannot reliably determine on which interface the limit was reached,
- // because the HAL interface does not specify it. We cannot just use the
- // current upstream, because that might have changed since the time that
- // the HAL queued the callback.
- // TODO: rev the HAL so that it provides an interface name.
-
- updateStatsForCurrentUpstream();
- if (mStatsProvider != null) {
- mStatsProvider.pushTetherStats();
- // Push stats to service does not cause the service react to it
- // immediately. Inform the service about limit reached.
- mStatsProvider.notifyLimitReached();
- }
- }
-
- @Override
- public void onNatTimeoutUpdate(int proto,
- String srcAddr, int srcPort,
- String dstAddr, int dstPort) {
- if (!started()) return;
- updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort);
- }
- });
-
- final boolean isStarted = started();
- if (!isStarted) {
- mLog.i("tethering offload control not supported");
- stop();
- } else {
- mLog.log("tethering offload started");
- mNatUpdateCallbacksReceived = 0;
- mNatUpdateNetlinkErrors = 0;
- maybeSchedulePollingStats();
- }
- return isStarted;
- }
-
- /** Stop hardware offload. */
- public void stop() {
- // Completely stops tethering offload. After this method is called, it is no longer safe to
- // call any HAL method, no callbacks from the hardware will be delivered, and any in-flight
- // callbacks must be ignored. Offload may be started again by calling start().
- final boolean wasStarted = started();
- updateStatsForCurrentUpstream();
- mUpstreamLinkProperties = null;
- mHwInterface.stopOffloadControl();
- mControlInitialized = false;
- mConfigInitialized = false;
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
- }
- if (wasStarted) mLog.log("tethering offload stopped");
- }
-
- private boolean started() {
- return mConfigInitialized && mControlInitialized;
- }
-
- @VisibleForTesting
- class OffloadTetheringStatsProvider extends NetworkStatsProvider {
- // These stats must only ever be touched on the handler thread.
- @NonNull
- private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
- @NonNull
- private NetworkStats mUidStats = new NetworkStats(0L, 0);
-
- /**
- * A helper function that collect tether stats from local hashmap. Note that this does not
- * invoke binder call.
- */
- @VisibleForTesting
- @NonNull
- NetworkStats getTetherStats(@NonNull StatsType how) {
- NetworkStats stats = new NetworkStats(0L, 0);
- final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
-
- for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
- final ForwardedStats value = kv.getValue();
- final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
- stats = stats.addEntry(entry);
- }
-
- return stats;
- }
-
- @Override
- public void onSetLimit(String iface, long quotaBytes) {
- // Listen for all iface is necessary since upstream might be changed after limit
- // is set.
- mHandler.post(() -> {
- final Long curIfaceQuota = mInterfaceQuotas.get(iface);
-
- // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE,
- // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not
- // useful to set it multiple times.
- // Otherwise, the quota needs to be updated to tell HAL to re-count from now even
- // if the quota is the same as the existing one.
- if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
-
- if (quotaBytes == QUOTA_UNLIMITED) {
- mInterfaceQuotas.remove(iface);
- } else {
- mInterfaceQuotas.put(iface, quotaBytes);
- }
- maybeUpdateDataLimit(iface);
- });
- }
-
- /**
- * Push stats to service, but does not cause a force polling. Note that this can only be
- * called on the handler thread.
- */
- public void pushTetherStats() {
- // TODO: remove the accumulated stats and report the diff from HAL directly.
- final NetworkStats ifaceDiff =
- getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
- final NetworkStats uidDiff =
- getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
- try {
- notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
- mIfaceStats = mIfaceStats.add(ifaceDiff);
- mUidStats = mUidStats.add(uidDiff);
- } catch (RuntimeException e) {
- mLog.e("Cannot report network stats: ", e);
- }
- }
-
- @Override
- public void onRequestStatsUpdate(int token) {
- // Do not attempt to update stats by querying the offload HAL
- // synchronously from a different thread than the Handler thread. http://b/64771555.
- mHandler.post(() -> {
- updateStatsForCurrentUpstream();
- pushTetherStats();
- });
- }
-
- @Override
- public void onSetAlert(long quotaBytes) {
- // TODO: Ask offload HAL to notify alert without stopping traffic.
- // Post it to handler thread since it access remaining quota bytes.
- mHandler.post(() -> {
- updateAlertQuota(quotaBytes);
- maybeSchedulePollingStats();
- });
- }
- }
-
- private String currentUpstreamInterface() {
- return (mUpstreamLinkProperties != null)
- ? mUpstreamLinkProperties.getInterfaceName() : null;
- }
-
- private void maybeUpdateStats(String iface) {
- if (TextUtils.isEmpty(iface)) {
- return;
- }
-
- // Always called on the handler thread.
- //
- // Use get()/put() instead of updating ForwardedStats in place because we can be called
- // concurrently with getTetherStats. In combination with the guarantees provided by
- // ConcurrentHashMap, this ensures that getTetherStats always gets the most recent copy of
- // the stats for each interface, and does not observe partial writes where rxBytes is
- // updated and txBytes is not.
- ForwardedStats diff = mHwInterface.getForwardedStats(iface);
- final long usedAlertQuota = diff.rxBytes + diff.txBytes;
- ForwardedStats base = mForwardedStats.get(iface);
- if (base != null) {
- diff.add(base);
- }
-
- // Update remaining alert quota if it is still positive.
- if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) {
- // Trim to zero if overshoot.
- final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0);
- updateAlertQuota(newQuota);
- }
-
- mForwardedStats.put(iface, diff);
- // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from
- // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately.
- }
-
- /**
- * Update remaining alert quota, fire the {@link NetworkStatsProvider#notifyAlertReached()}
- * callback when it reaches zero. This can be invoked either from service setting the alert, or
- * {@code maybeUpdateStats} when updating stats. Note that this can be only called on
- * handler thread.
- *
- * @param newQuota non-negative value to indicate the new quota, or
- * {@link NetworkStatsProvider#QUOTA_UNLIMITED} to indicate there is no
- * quota.
- */
- private void updateAlertQuota(long newQuota) {
- if (newQuota < QUOTA_UNLIMITED) {
- throw new IllegalArgumentException("invalid quota value " + newQuota);
- }
- if (mRemainingAlertQuota == newQuota) return;
-
- mRemainingAlertQuota = newQuota;
- if (mRemainingAlertQuota == 0) {
- mLog.i("notifyAlertReached");
- if (mStatsProvider != null) mStatsProvider.notifyAlertReached();
- }
- }
-
- /**
- * Schedule polling if needed, this will be stopped if offload has been
- * stopped or remaining quota reaches zero or upstream is empty.
- * Note that this can be only called on handler thread.
- */
- private void maybeSchedulePollingStats() {
- if (!isPollingStatsNeeded()) return;
-
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
- }
- mHandler.postDelayed(mScheduledPollingTask,
- mDeps.getTetherConfig().getOffloadPollInterval());
- }
-
- private boolean isPollingStatsNeeded() {
- return started() && mRemainingAlertQuota > 0
- && !TextUtils.isEmpty(currentUpstreamInterface())
- && mDeps.getTetherConfig() != null
- && mDeps.getTetherConfig().getOffloadPollInterval()
- >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
- }
-
- private boolean maybeUpdateDataLimit(String iface) {
- // setDataLimit may only be called while offload is occurring on this upstream.
- if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
- return true;
- }
-
- Long limit = mInterfaceQuotas.get(iface);
- if (limit == null) {
- limit = Long.MAX_VALUE;
- }
-
- return mHwInterface.setDataLimit(iface, limit);
- }
-
- private void updateStatsForCurrentUpstream() {
- maybeUpdateStats(currentUpstreamInterface());
- }
-
- private void updateStatsForAllUpstreams() {
- // In practice, there should only ever be a single digit number of
- // upstream interfaces over the lifetime of an active tethering session.
- // Roughly speaking, imagine a very ambitious one or two of each of the
- // following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ].
- for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
- maybeUpdateStats(kv.getKey());
- }
- }
-
- /** Set current tethering upstream LinkProperties. */
- public void setUpstreamLinkProperties(LinkProperties lp) {
- if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
-
- final String prevUpstream = currentUpstreamInterface();
-
- mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null;
- // Make sure we record this interface in the ForwardedStats map.
- final String iface = currentUpstreamInterface();
- if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS);
-
- maybeSchedulePollingStats();
-
- // TODO: examine return code and decide what to do if programming
- // upstream parameters fails (probably just wait for a subsequent
- // onOffloadEvent() callback to tell us offload is available again and
- // then reapply all state).
- computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
- pushUpstreamParameters(prevUpstream);
- }
-
- /** Set local prefixes. */
- public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
- mExemptPrefixes = localPrefixes;
-
- if (!started()) return;
- computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
- }
-
- /** Update current downstream LinkProperties. */
- public void notifyDownstreamLinkProperties(LinkProperties lp) {
- final String ifname = lp.getInterfaceName();
- final LinkProperties oldLp = mDownstreams.put(ifname, new LinkProperties(lp));
- if (Objects.equals(oldLp, lp)) return;
-
- if (!started()) return;
- pushDownstreamState(oldLp, lp);
- }
-
- private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) {
- final String ifname = newLp.getInterfaceName();
- final List<RouteInfo> oldRoutes =
- (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST;
- final List<RouteInfo> newRoutes = newLp.getRoutes();
-
- // For each old route, if not in new routes: remove.
- for (RouteInfo ri : oldRoutes) {
- if (shouldIgnoreDownstreamRoute(ri)) continue;
- if (!newRoutes.contains(ri)) {
- mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString());
- }
- }
-
- // For each new route, if not in old routes: add.
- for (RouteInfo ri : newRoutes) {
- if (shouldIgnoreDownstreamRoute(ri)) continue;
- if (!oldRoutes.contains(ri)) {
- mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString());
- }
- }
- }
-
- private void pushAllDownstreamState() {
- for (LinkProperties lp : mDownstreams.values()) {
- pushDownstreamState(null, lp);
- }
- }
-
- /** Remove downstream interface from offload hardware. */
- public void removeDownstreamInterface(String ifname) {
- final LinkProperties lp = mDownstreams.remove(ifname);
- if (lp == null) return;
-
- if (!started()) return;
-
- for (RouteInfo route : lp.getRoutes()) {
- if (shouldIgnoreDownstreamRoute(route)) continue;
- mHwInterface.removeDownstreamPrefix(ifname, route.getDestination().toString());
- }
- }
-
- private boolean isOffloadDisabled() {
- final int defaultDisposition = mHwInterface.getDefaultTetherOffloadDisabled();
- return (Settings.Global.getInt(
- mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0);
- }
-
- private boolean pushUpstreamParameters(String prevUpstream) {
- final String iface = currentUpstreamInterface();
-
- if (TextUtils.isEmpty(iface)) {
- final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null);
- // Update stats after we've told the hardware to stop forwarding so
- // we don't miss packets.
- maybeUpdateStats(prevUpstream);
- return rval;
- }
-
- // A stacked interface cannot be an upstream for hardware offload.
- // Consequently, we examine only the primary interface name, look at
- // getAddresses() rather than getAllAddresses(), and check getRoutes()
- // rather than getAllRoutes().
- final ArrayList<String> v6gateways = new ArrayList<>();
- String v4addr = null;
- String v4gateway = null;
-
- for (InetAddress ip : mUpstreamLinkProperties.getAddresses()) {
- if (ip instanceof Inet4Address) {
- v4addr = ip.getHostAddress();
- break;
- }
- }
-
- // Find the gateway addresses of all default routes of either address family.
- for (RouteInfo ri : mUpstreamLinkProperties.getRoutes()) {
- if (!ri.hasGateway()) continue;
-
- final String gateway = ri.getGateway().getHostAddress();
- final InetAddress address = ri.getDestination().getAddress();
- if (ri.isDefaultRoute() && address instanceof Inet4Address) {
- v4gateway = gateway;
- } else if (ri.isDefaultRoute() && address instanceof Inet6Address) {
- v6gateways.add(gateway);
- }
- }
-
- boolean success = mHwInterface.setUpstreamParameters(
- iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
-
- if (!success) {
- return success;
- }
-
- // Update stats after we've told the hardware to change routing so we don't miss packets.
- maybeUpdateStats(prevUpstream);
-
- // Data limits can only be set once offload is running on the upstream.
- success = maybeUpdateDataLimit(iface);
- if (!success) {
- // If we failed to set a data limit, don't use this upstream, because we don't want to
- // blow through the data limit that we were told to apply.
- mLog.log("Setting data limit for " + iface + " failed, disabling offload.");
- stop();
- }
-
- return success;
- }
-
- private boolean computeAndPushLocalPrefixes(UpdateType how) {
- final boolean force = (how == UpdateType.FORCE);
- final Set<String> localPrefixStrs = computeLocalPrefixStrings(
- mExemptPrefixes, mUpstreamLinkProperties);
- if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
-
- mLastLocalPrefixStrs = localPrefixStrs;
- return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs));
- }
-
- // TODO: Factor in downstream LinkProperties once that information is available.
- private static Set<String> computeLocalPrefixStrings(
- Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) {
- // Create an editable copy.
- final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes);
-
- // TODO: If a downstream interface (not currently passed in) is reusing
- // the /64 of the upstream (64share) then:
- //
- // [a] remove that /64 from the local prefixes
- // [b] add in /128s for IP addresses on the downstream interface
- // [c] add in /128s for IP addresses on the upstream interface
- //
- // Until downstream information is available here, simply add /128s from
- // the upstream network; they'll just be redundant with their /64.
- if (upstreamLinkProperties != null) {
- for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) {
- if (!linkAddr.isGlobalPreferred()) continue;
- final InetAddress ip = linkAddr.getAddress();
- if (!(ip instanceof Inet6Address)) continue;
- prefixSet.add(new IpPrefix(ip, 128));
- }
- }
-
- final HashSet<String> localPrefixStrs = new HashSet<>();
- for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString());
- return localPrefixStrs;
- }
-
- private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) {
- // Ignore any link-local routes.
- final IpPrefix destination = route.getDestination();
- final LinkAddress linkAddr = new LinkAddress(destination.getAddress(),
- destination.getPrefixLength());
- if (!linkAddr.isGlobalPreferred()) return true;
-
- return false;
- }
-
- /** Dump information. */
- public void dump(IndentingPrintWriter pw) {
- if (isOffloadDisabled()) {
- pw.println("Offload disabled");
- return;
- }
- final boolean isStarted = started();
- pw.println("Offload HALs " + (isStarted ? "started" : "not started"));
- LinkProperties lp = mUpstreamLinkProperties;
- String upstream = (lp != null) ? lp.getInterfaceName() : null;
- pw.println("Current upstream: " + upstream);
- pw.println("Exempt prefixes: " + mLastLocalPrefixStrs);
- pw.println("NAT timeout update callbacks received during the "
- + (isStarted ? "current" : "last")
- + " offload session: "
- + mNatUpdateCallbacksReceived);
- pw.println("NAT timeout update netlink errors during the "
- + (isStarted ? "current" : "last")
- + " offload session: "
- + mNatUpdateNetlinkErrors);
- }
-
- private void updateNatTimeout(
- int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) {
- final String protoName = protoNameFor(proto);
- if (protoName == null) {
- mLog.e("Unknown NAT update callback protocol: " + proto);
- return;
- }
-
- final Inet4Address src = parseIPv4Address(srcAddr);
- if (src == null) {
- mLog.e("Failed to parse IPv4 address: " + srcAddr);
- return;
- }
-
- if (!isValidUdpOrTcpPort(srcPort)) {
- mLog.e("Invalid src port: " + srcPort);
- return;
- }
-
- final Inet4Address dst = parseIPv4Address(dstAddr);
- if (dst == null) {
- mLog.e("Failed to parse IPv4 address: " + dstAddr);
- return;
- }
-
- if (!isValidUdpOrTcpPort(dstPort)) {
- mLog.e("Invalid dst port: " + dstPort);
- return;
- }
-
- mNatUpdateCallbacksReceived++;
- final String natDescription = String.format("%s (%s, %s) -> (%s, %s)",
- protoName, srcAddr, srcPort, dstAddr, dstPort);
- if (DBG) {
- mLog.log("NAT timeout update: " + natDescription);
- }
-
- final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto);
- final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest(
- proto, src, srcPort, dst, dstPort, timeoutSec);
-
- try {
- NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
- } catch (ErrnoException e) {
- mNatUpdateNetlinkErrors++;
- mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e
- + ", msg: " + NetlinkConstants.hexify(msg));
- mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived);
- mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors);
- }
- }
-
- private static Inet4Address parseIPv4Address(String addrString) {
- try {
- final InetAddress ip = InetAddresses.parseNumericAddress(addrString);
- // TODO: Consider other sanitization steps here, including perhaps:
- // not eql to 0.0.0.0
- // not within 169.254.0.0/16
- // not within ::ffff:0.0.0.0/96
- // not within ::/96
- // et cetera.
- if (ip instanceof Inet4Address) {
- return (Inet4Address) ip;
- }
- } catch (IllegalArgumentException iae) { }
- return null;
- }
-
- private static String protoNameFor(int proto) {
- // OsConstants values are not constant expressions; no switch statement.
- if (proto == OsConstants.IPPROTO_UDP) {
- return "UDP";
- } else if (proto == OsConstants.IPPROTO_TCP) {
- return "TCP";
- }
- return null;
- }
-
- private static int connectionTimeoutUpdateSecondsFor(int proto) {
- // TODO: Replace this with more thoughtful work, perhaps reading from
- // and maybe writing to any required
- //
- // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_*
- // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream}
- //
- // entries. TBD.
- if (proto == OsConstants.IPPROTO_TCP) {
- // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
- return 432000;
- } else {
- // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
- return 180;
- }
- }
-
- private static boolean isValidUdpOrTcpPort(int port) {
- return port > 0 && port < 65536;
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
deleted file mode 100644
index da5f25b2a596..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
-import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-import static android.net.util.TetheringUtils.uint16;
-
-import android.annotation.NonNull;
-import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
-import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
-import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
-import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
-import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
-import android.net.netlink.NetlinkSocket;
-import android.net.netlink.StructNfGenMsg;
-import android.net.netlink.StructNlMsgHdr;
-import android.net.util.SharedLog;
-import android.net.util.SocketUtils;
-import android.os.Handler;
-import android.os.NativeHandle;
-import android.os.RemoteException;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
-
-
-/**
- * Capture tethering dependencies, for injection.
- *
- * @hide
- */
-public class OffloadHardwareInterface {
- private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
- private static final String YIELDS = " -> ";
- // Change this value to control whether tether offload is enabled or
- // disabled by default in the absence of an explicit Settings value.
- // See accompanying unittest to distinguish 0 from non-0 values.
- private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0;
- private static final String NO_INTERFACE_NAME = "";
- private static final String NO_IPV4_ADDRESS = "";
- private static final String NO_IPV4_GATEWAY = "";
- // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h
- public static final int NF_NETLINK_CONNTRACK_NEW = 1;
- public static final int NF_NETLINK_CONNTRACK_UPDATE = 2;
- public static final int NF_NETLINK_CONNTRACK_DESTROY = 4;
- // Reference libnetfilter_conntrack/linux_nfnetlink_conntrack.h
- public static final short NFNL_SUBSYS_CTNETLINK = 1;
- public static final short IPCTNL_MSG_CT_NEW = 0;
- public static final short IPCTNL_MSG_CT_GET = 1;
-
- private final long NETLINK_MESSAGE_TIMEOUT_MS = 500;
-
- private final Handler mHandler;
- private final SharedLog mLog;
- private final Dependencies mDeps;
- private IOffloadControl mOffloadControl;
- private TetheringOffloadCallback mTetheringOffloadCallback;
- private ControlCallback mControlCallback;
-
- /** The callback to notify status of offload management process. */
- public static class ControlCallback {
- /** Offload started. */
- public void onStarted() {}
- /**
- * Offload stopped because an error has occurred in lower layer.
- */
- public void onStoppedError() {}
- /**
- * Offload stopped because the device has moved to a bearer on which hardware offload is
- * not supported. Subsequent calls to setUpstreamParameters and add/removeDownstream will
- * likely fail and cannot be presumed to be saved inside of the hardware management process.
- * Upon receiving #onSupportAvailable(), the caller should reprogram the hardware to begin
- * offload again.
- */
- public void onStoppedUnsupported() {}
- /** Indicate that offload is able to proivde support for this time. */
- public void onSupportAvailable() {}
- /** Offload stopped because of usage limit reached. */
- public void onStoppedLimitReached() {}
-
- /** Indicate to update NAT timeout. */
- public void onNatTimeoutUpdate(int proto,
- String srcAddr, int srcPort,
- String dstAddr, int dstPort) {}
- }
-
- /** The object which records Tx/Rx forwarded bytes. */
- public static class ForwardedStats {
- public long rxBytes;
- public long txBytes;
-
- public ForwardedStats() {
- rxBytes = 0;
- txBytes = 0;
- }
-
- @VisibleForTesting
- public ForwardedStats(long rxBytes, long txBytes) {
- this.rxBytes = rxBytes;
- this.txBytes = txBytes;
- }
-
- /** Add Tx/Rx bytes. */
- public void add(ForwardedStats other) {
- rxBytes += other.rxBytes;
- txBytes += other.txBytes;
- }
-
- /** Returns the string representation of this object. */
- public String toString() {
- return String.format("rx:%s tx:%s", rxBytes, txBytes);
- }
- }
-
- public OffloadHardwareInterface(Handler h, SharedLog log) {
- this(h, log, new Dependencies(log));
- }
-
- OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) {
- mHandler = h;
- mLog = log.forSubComponent(TAG);
- mDeps = deps;
- }
-
- /** Capture OffloadHardwareInterface dependencies, for injection. */
- static class Dependencies {
- private final SharedLog mLog;
-
- Dependencies(SharedLog log) {
- mLog = log;
- }
-
- public IOffloadConfig getOffloadConfig() {
- try {
- return IOffloadConfig.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("getIOffloadConfig error " + e);
- return null;
- }
- }
-
- public IOffloadControl getOffloadControl() {
- try {
- return IOffloadControl.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("tethering offload control not supported: " + e);
- return null;
- }
- }
-
- public NativeHandle createConntrackSocket(final int groups) {
- final FileDescriptor fd;
- try {
- fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
- } catch (ErrnoException e) {
- mLog.e("Unable to create conntrack socket " + e);
- return null;
- }
-
- final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
- try {
- Os.bind(fd, sockAddr);
- } catch (ErrnoException | SocketException e) {
- mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ie) {
- // Nothing we can do here
- }
- return null;
- }
- try {
- Os.connect(fd, sockAddr);
- } catch (ErrnoException | SocketException e) {
- mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ie) {
- // Nothing we can do here
- }
- return null;
- }
-
- return new NativeHandle(fd, true);
- }
- }
-
- /** Get default value indicating whether offload is supported. */
- public int getDefaultTetherOffloadDisabled() {
- return DEFAULT_TETHER_OFFLOAD_DISABLED;
- }
-
- /**
- * Offload management process need to know conntrack rules to support NAT, but it may not have
- * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
- * share them with offload management process.
- */
- public boolean initOffloadConfig() {
- final IOffloadConfig offloadConfig = mDeps.getOffloadConfig();
- if (offloadConfig == null) {
- mLog.e("Could not find IOffloadConfig service");
- return false;
- }
- // Per the IConfigOffload definition:
- //
- // h1 provides a file descriptor bound to the following netlink groups
- // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
- //
- // h2 provides a file descriptor bound to the following netlink groups
- // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
- final NativeHandle h1 = mDeps.createConntrackSocket(
- NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
- if (h1 == null) return false;
-
- sendIpv4NfGenMsg(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
- (short) (NLM_F_REQUEST | NLM_F_DUMP));
-
- final NativeHandle h2 = mDeps.createConntrackSocket(
- NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
- if (h2 == null) {
- closeFdInNativeHandle(h1);
- return false;
- }
-
- final CbResults results = new CbResults();
- try {
- offloadConfig.setHandles(h1, h2,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record("initOffloadConfig, setHandles fail", e);
- return false;
- }
- // Explicitly close FDs.
- closeFdInNativeHandle(h1);
- closeFdInNativeHandle(h2);
-
- record("initOffloadConfig, setHandles results:", results);
- return results.mSuccess;
- }
-
- @VisibleForTesting
- public void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) {
- final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
- final byte[] msg = new byte[length];
- final ByteBuffer byteBuffer = ByteBuffer.wrap(msg);
- byteBuffer.order(ByteOrder.nativeOrder());
-
- final StructNlMsgHdr nlh = new StructNlMsgHdr();
- nlh.nlmsg_len = length;
- nlh.nlmsg_type = type;
- nlh.nlmsg_flags = flags;
- nlh.nlmsg_seq = 0;
- nlh.pack(byteBuffer);
-
- // Header needs to be added to buffer since a generic netlink request is being sent.
- final StructNfGenMsg nfh = new StructNfGenMsg((byte) OsConstants.AF_INET);
- nfh.pack(byteBuffer);
-
- try {
- NetlinkSocket.sendMessage(handle.getFileDescriptor(), msg, 0 /* offset */, length,
- NETLINK_MESSAGE_TIMEOUT_MS);
- } catch (ErrnoException | InterruptedIOException e) {
- mLog.e("Unable to send netfilter message, error: " + e);
- }
- }
-
- private void closeFdInNativeHandle(final NativeHandle h) {
- try {
- h.close();
- } catch (IOException | IllegalStateException e) {
- // IllegalStateException means fd is already closed, do nothing here.
- // Also nothing we can do if IOException.
- }
- }
-
- /** Initialize the tethering offload HAL. */
- public boolean initOffloadControl(ControlCallback controlCb) {
- mControlCallback = controlCb;
-
- if (mOffloadControl == null) {
- mOffloadControl = mDeps.getOffloadControl();
- if (mOffloadControl == null) {
- mLog.e("tethering IOffloadControl.getService() returned null");
- return false;
- }
- }
-
- final String logmsg = String.format("initOffloadControl(%s)",
- (controlCb == null) ? "null"
- : "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
-
- mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog);
- final CbResults results = new CbResults();
- try {
- mOffloadControl.initOffload(
- mTetheringOffloadCallback,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Stop IOffloadControl. */
- public void stopOffloadControl() {
- if (mOffloadControl != null) {
- try {
- mOffloadControl.stopOffload(
- (boolean success, String errMsg) -> {
- if (!success) mLog.e("stopOffload failed: " + errMsg);
- });
- } catch (RemoteException e) {
- mLog.e("failed to stopOffload: " + e);
- }
- }
- mOffloadControl = null;
- mTetheringOffloadCallback = null;
- mControlCallback = null;
- mLog.log("stopOffloadControl()");
- }
-
- /** Get Tx/Rx usage from last query. */
- public ForwardedStats getForwardedStats(String upstream) {
- final String logmsg = String.format("getForwardedStats(%s)", upstream);
-
- final ForwardedStats stats = new ForwardedStats();
- try {
- mOffloadControl.getForwardedStats(
- upstream,
- (long rxBytes, long txBytes) -> {
- stats.rxBytes = (rxBytes > 0) ? rxBytes : 0;
- stats.txBytes = (txBytes > 0) ? txBytes : 0;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return stats;
- }
-
- return stats;
- }
-
- /** Set local prefixes to offload management process. */
- public boolean setLocalPrefixes(ArrayList<String> localPrefixes) {
- final String logmsg = String.format("setLocalPrefixes([%s])",
- String.join(",", localPrefixes));
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setLocalPrefixes(localPrefixes,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Set data limit value to offload management process. */
- public boolean setDataLimit(String iface, long limit) {
-
- final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setDataLimit(
- iface, limit,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Set upstream parameters to offload management process. */
- public boolean setUpstreamParameters(
- String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
- iface = (iface != null) ? iface : NO_INTERFACE_NAME;
- v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS;
- v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY;
- v6gws = (v6gws != null) ? v6gws : new ArrayList<>();
-
- final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
- iface, v4addr, v4gateway, String.join(",", v6gws));
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setUpstreamParameters(
- iface, v4addr, v4gateway, v6gws,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Add downstream prefix to offload management process. */
- public boolean addDownstreamPrefix(String ifname, String prefix) {
- final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.addDownstream(ifname, prefix,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- /** Remove downstream prefix from offload management process. */
- public boolean removeDownstreamPrefix(String ifname, String prefix) {
- final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.removeDownstream(ifname, prefix,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- private void record(String msg, Throwable t) {
- mLog.e(msg + YIELDS + "exception: " + t);
- }
-
- private void record(String msg, CbResults results) {
- final String logmsg = msg + YIELDS + results;
- if (!results.mSuccess) {
- mLog.e(logmsg);
- } else {
- mLog.log(logmsg);
- }
- }
-
- private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
- public final Handler handler;
- public final ControlCallback controlCb;
- public final SharedLog log;
-
- TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) {
- handler = h;
- controlCb = cb;
- log = sharedLog;
- }
-
- @Override
- public void onEvent(int event) {
- handler.post(() -> {
- switch (event) {
- case OffloadCallbackEvent.OFFLOAD_STARTED:
- controlCb.onStarted();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
- controlCb.onStoppedError();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
- controlCb.onStoppedUnsupported();
- break;
- case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
- controlCb.onSupportAvailable();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
- controlCb.onStoppedLimitReached();
- break;
- default:
- log.e("Unsupported OffloadCallbackEvent: " + event);
- }
- });
- }
-
- @Override
- public void updateTimeout(NatTimeoutUpdate params) {
- handler.post(() -> {
- controlCb.onNatTimeoutUpdate(
- networkProtocolToOsConstant(params.proto),
- params.src.addr, uint16(params.src.port),
- params.dst.addr, uint16(params.dst.port));
- });
- }
- }
-
- private static int networkProtocolToOsConstant(int proto) {
- switch (proto) {
- case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
- case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
- default:
- // The caller checks this value and will log an error. Just make
- // sure it won't collide with valid OsContants.IPPROTO_* values.
- return -Math.abs(proto);
- }
- }
-
- private static class CbResults {
- boolean mSuccess;
- String mErrMsg;
-
- @Override
- public String toString() {
- if (mSuccess) {
- return "ok";
- } else {
- return "fail: " + mErrMsg;
- }
- }
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
deleted file mode 100644
index 4f616cdff086..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ /dev/null
@@ -1,416 +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.networkstack.tethering;
-
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
-import static android.net.TetheringManager.TETHERING_BLUETOOTH;
-import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.util.PrefixUtils.asIpPrefix;
-
-import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
-import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
-import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
-
-import static java.util.Arrays.asList;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.Network;
-import android.net.ip.IpServer;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * This class coordinate IP addresses conflict problem.
- *
- * Tethering downstream IP addresses may conflict with network assigned addresses. This
- * coordinator is responsible for recording all of network assigned addresses and dispatched
- * free address to downstream interfaces.
- *
- * This class is not thread-safe and should be accessed on the same tethering internal thread.
- * @hide
- */
-public class PrivateAddressCoordinator {
- public static final int PREFIX_LENGTH = 24;
-
- // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
- // address may be requested before coordinator get current upstream notification. To ensure
- // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
- // when tethering is down. Instead tethering would remove all deprecated upstreams from
- // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams().
- private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
- private final ArraySet<IpServer> mDownstreams;
- private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
- private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24";
- private final List<IpPrefix> mTetheringPrefixes;
- private final ConnectivityManager mConnectivityMgr;
- private final TetheringConfiguration mConfig;
- // keyed by downstream type(TetheringManager.TETHERING_*).
- private final SparseArray<LinkAddress> mCachedAddresses;
-
- public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
- mDownstreams = new ArraySet<>();
- mUpstreamPrefixMap = new ArrayMap<>();
- mConnectivityMgr = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- mConfig = config;
- mCachedAddresses = new SparseArray<>();
- // Reserved static addresses for bluetooth and wifi p2p.
- mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
- mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
-
- mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16")));
- if (config.isSelectAllPrefixRangeEnabled()) {
- mTetheringPrefixes.add(new IpPrefix("172.16.0.0/12"));
- mTetheringPrefixes.add(new IpPrefix("10.0.0.0/8"));
- }
- }
-
- /**
- * Record a new upstream IpPrefix which may conflict with tethering downstreams.
- * The downstreams will be notified if a conflict is found. When updateUpstreamPrefix is called,
- * UpstreamNetworkState must have an already populated LinkProperties.
- */
- public void updateUpstreamPrefix(final UpstreamNetworkState ns) {
- // Do not support VPN as upstream. Normally, networkCapabilities is not expected to be null,
- // but just checking to be sure.
- if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
- removeUpstreamPrefix(ns.network);
- return;
- }
-
- final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(
- ns.linkProperties.getAllLinkAddresses());
- if (ipv4Prefixes.isEmpty()) {
- removeUpstreamPrefix(ns.network);
- return;
- }
-
- mUpstreamPrefixMap.put(ns.network, ipv4Prefixes);
- handleMaybePrefixConflict(ipv4Prefixes);
- }
-
- private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) {
- final ArrayList<IpPrefix> list = new ArrayList<>();
- for (LinkAddress address : linkAddresses) {
- if (!address.isIpv4()) continue;
-
- list.add(asIpPrefix(address));
- }
-
- return list;
- }
-
- private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
- for (IpServer downstream : mDownstreams) {
- final IpPrefix target = getDownstreamPrefix(downstream);
-
- for (IpPrefix source : prefixes) {
- if (isConflictPrefix(source, target)) {
- downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- break;
- }
- }
- }
- }
-
- /** Remove IpPrefix records corresponding to input network. */
- public void removeUpstreamPrefix(final Network network) {
- mUpstreamPrefixMap.remove(network);
- }
-
- /**
- * Maybe remove deprecated upstream records, this would be called once tethering started without
- * any exiting tethered downstream.
- */
- public void maybeRemoveDeprecatedUpstreams() {
- if (mUpstreamPrefixMap.isEmpty()) return;
-
- // Remove all upstreams that are no longer valid networks
- final Set<Network> toBeRemoved = new HashSet<>(mUpstreamPrefixMap.keySet());
- toBeRemoved.removeAll(asList(mConnectivityMgr.getAllNetworks()));
-
- mUpstreamPrefixMap.removeAll(toBeRemoved);
- }
-
- /**
- * Pick a random available address and mark its prefix as in use for the provided IpServer,
- * returns null if there is no available address.
- */
- @Nullable
- public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
- if (mConfig.shouldEnableWifiP2pDedicatedIp()
- && ipServer.interfaceType() == TETHERING_WIFI_P2P) {
- return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
- }
-
- final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType());
- if (useLastAddress && cachedAddress != null
- && !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
- mDownstreams.add(ipServer);
- return cachedAddress;
- }
-
- for (IpPrefix prefixRange : mTetheringPrefixes) {
- final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
- if (newAddress != null) {
- mDownstreams.add(ipServer);
- mCachedAddresses.put(ipServer.interfaceType(), newAddress);
- return newAddress;
- }
- }
-
- // No available address.
- return null;
- }
-
- private int getPrefixBaseAddress(final IpPrefix prefix) {
- return inet4AddressToIntHTH((Inet4Address) prefix.getAddress());
- }
-
- /**
- * Check whether input prefix conflict with upstream prefixes or in-use downstream prefixes.
- * If yes, return one of them.
- */
- private IpPrefix getConflictPrefix(final IpPrefix prefix) {
- final IpPrefix upstream = getConflictWithUpstream(prefix);
- if (upstream != null) return upstream;
-
- return getInUseDownstreamPrefix(prefix);
- }
-
- // Get the next non-conflict sub prefix. E.g: To get next sub prefix from 10.0.0.0/8, if the
- // previously selected prefix is 10.20.42.0/24(subPrefix: 0.20.42.0) and the conflicting prefix
- // is 10.16.0.0/20 (10.16.0.0 ~ 10.16.15.255), then the max address under subPrefix is
- // 0.16.15.255 and the next subPrefix is 0.16.16.255/24 (0.16.15.255 + 0.0.1.0).
- // Note: the sub address 0.0.0.255 here is fine to be any value that it will be replaced as
- // selected random sub address later.
- private int getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask) {
- final int suffixMask = ~prefixLengthToV4NetmaskIntHTH(conflictPrefix.getPrefixLength());
- // The largest offset within the prefix assignment block that still conflicts with
- // conflictPrefix.
- final int maxConflict =
- (getPrefixBaseAddress(conflictPrefix) | suffixMask) & ~prefixRangeMask;
-
- final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
- // Pick a sub prefix a full prefix (1 << (32 - PREFIX_LENGTH) addresses) greater than
- // maxConflict. This ensures that the selected prefix never overlaps with conflictPrefix.
- // There is no need to mask the result with PREFIX_LENGTH bits because this is done by
- // findAvailablePrefixFromRange when it constructs the prefix.
- return maxConflict + (1 << (32 - PREFIX_LENGTH));
- }
-
- private LinkAddress chooseDownstreamAddress(final IpPrefix prefixRange) {
- // The netmask of the prefix assignment block (e.g., 0xfff00000 for 172.16.0.0/12).
- final int prefixRangeMask = prefixLengthToV4NetmaskIntHTH(prefixRange.getPrefixLength());
-
- // The zero address in the block (e.g., 0xac100000 for 172.16.0.0/12).
- final int baseAddress = getPrefixBaseAddress(prefixRange);
-
- // The subnet mask corresponding to PREFIX_LENGTH.
- final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
-
- // The offset within prefixRange of a randomly-selected prefix of length PREFIX_LENGTH.
- // This may not be the prefix of the address returned by this method:
- // - If it is already in use, the method will return an address in another prefix.
- // - If all prefixes within prefixRange are in use, the method will return null. For
- // example, for a /24 prefix within 172.26.0.0/12, this will be a multiple of 256 in
- // [0, 1048576). In other words, a random 32-bit number with mask 0x000fff00.
- //
- // prefixRangeMask is required to ensure no wrapping. For example, consider:
- // - prefixRange 127.0.0.0/8
- // - randomPrefixStart 127.255.255.0
- // - A conflicting prefix of 127.255.254.0/23
- // In this case without prefixRangeMask, getNextSubPrefix would return 128.0.0.0, which
- // means the "start < end" check in findAvailablePrefixFromRange would not reject the prefix
- // because Java doesn't have unsigned integers, so 128.0.0.0 = 0x80000000 = -2147483648
- // is less than 127.0.0.0 = 0x7f000000 = 2130706432.
- //
- // Additionally, it makes debug output easier to read by making the numbers smaller.
- final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask;
-
- // A random offset within the prefix. Used to determine the local address once the prefix
- // is selected. It does not result in an IPv4 address ending in .0, .1, or .255
- // For a PREFIX_LENGTH of 255, this is a number between 2 and 254.
- final int subAddress = getSanitizedSubAddr(~prefixMask);
-
- // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block,
- // such that the prefix does not conflict with any upstream.
- IpPrefix downstreamPrefix = findAvailablePrefixFromRange(
- randomPrefixStart, (~prefixRangeMask) + 1, baseAddress, prefixRangeMask);
- if (downstreamPrefix != null) return getLinkAddress(downstreamPrefix, subAddress);
-
- // If that failed, do the same, but between 0 and randomPrefixStart.
- downstreamPrefix = findAvailablePrefixFromRange(
- 0, randomPrefixStart, baseAddress, prefixRangeMask);
-
- return getLinkAddress(downstreamPrefix, subAddress);
- }
-
- private LinkAddress getLinkAddress(final IpPrefix prefix, final int subAddress) {
- if (prefix == null) return null;
-
- final InetAddress address = intToInet4AddressHTH(getPrefixBaseAddress(prefix) | subAddress);
- return new LinkAddress(address, PREFIX_LENGTH);
- }
-
- private IpPrefix findAvailablePrefixFromRange(final int start, final int end,
- final int baseAddress, final int prefixRangeMask) {
- int newSubPrefix = start;
- while (newSubPrefix < end) {
- final InetAddress address = intToInet4AddressHTH(baseAddress | newSubPrefix);
- final IpPrefix prefix = new IpPrefix(address, PREFIX_LENGTH);
-
- final IpPrefix conflictPrefix = getConflictPrefix(prefix);
-
- if (conflictPrefix == null) return prefix;
-
- newSubPrefix = getNextSubPrefix(conflictPrefix, prefixRangeMask);
- }
-
- return null;
- }
-
- /** Get random int which could be used to generate random address. */
- @VisibleForTesting
- public int getRandomInt() {
- return (new Random()).nextInt();
- }
-
- /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */
- private int getSanitizedSubAddr(final int subAddrMask) {
- final int randomSubAddr = getRandomInt() & subAddrMask;
- // If prefix length > 30, the selecting speace would be less than 4 which may be hard to
- // avoid 3 consecutive address.
- if (PREFIX_LENGTH > 30) return randomSubAddr;
-
- // TODO: maybe it is not necessary to avoid .0, .1 and .255 address because tethering
- // address would not be conflicted. This code only works because PREFIX_LENGTH is not longer
- // than 24
- final int candidate = randomSubAddr & 0xff;
- if (candidate == 0 || candidate == 1 || candidate == 255) {
- return (randomSubAddr & 0xfffffffc) + 2;
- }
-
- return randomSubAddr;
- }
-
- /** Release downstream record for IpServer. */
- public void releaseDownstream(final IpServer ipServer) {
- mDownstreams.remove(ipServer);
- }
-
- /** Clear current upstream prefixes records. */
- public void clearUpstreamPrefixes() {
- mUpstreamPrefixMap.clear();
- }
-
- private IpPrefix getConflictWithUpstream(final IpPrefix prefix) {
- for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
- final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
- for (IpPrefix upstream : list) {
- if (isConflictPrefix(prefix, upstream)) return upstream;
- }
- }
- return null;
- }
-
- private boolean isConflictWithUpstream(final IpPrefix prefix) {
- return getConflictWithUpstream(prefix) != null;
- }
-
- private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
- if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) {
- return prefix2.contains(prefix1.getAddress());
- }
-
- return prefix1.contains(prefix2.getAddress());
- }
-
- // InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last
- // downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p).
- private IpPrefix getInUseDownstreamPrefix(final IpPrefix prefix) {
- for (int i = 0; i < mCachedAddresses.size(); i++) {
- final IpPrefix downstream = asIpPrefix(mCachedAddresses.valueAt(i));
- if (isConflictPrefix(prefix, downstream)) return downstream;
- }
-
- // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
- // in mCachedAddresses.
- for (IpServer downstream : mDownstreams) {
- final IpPrefix target = getDownstreamPrefix(downstream);
-
- if (isConflictPrefix(prefix, target)) return target;
- }
-
- return null;
- }
-
- @NonNull
- private IpPrefix getDownstreamPrefix(final IpServer downstream) {
- final LinkAddress address = downstream.getAddress();
-
- return asIpPrefix(address);
- }
-
- void dump(final IndentingPrintWriter pw) {
- pw.println("mTetheringPrefixes:");
- pw.increaseIndent();
- for (IpPrefix prefix : mTetheringPrefixes) {
- pw.println(prefix);
- }
- pw.decreaseIndent();
-
- pw.println("mUpstreamPrefixMap:");
- pw.increaseIndent();
- for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
- pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
- }
- pw.decreaseIndent();
-
- pw.println("mDownstreams:");
- pw.increaseIndent();
- for (IpServer ipServer : mDownstreams) {
- pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
- }
- pw.decreaseIndent();
-
- pw.println("mCachedAddresses:");
- pw.increaseIndent();
- for (int i = 0; i < mCachedAddresses.size(); i++) {
- pw.println(mCachedAddresses.keyAt(i) + " - " + mCachedAddresses.valueAt(i));
- }
- pw.decreaseIndent();
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
deleted file mode 100644
index 5a0c5b0cff5f..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ /dev/null
@@ -1,2427 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.Manifest.permission.NETWORK_SETTINGS;
-import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.PackageManager.GET_ACTIVITIES;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.hardware.usb.UsbManager.USB_CONFIGURED;
-import static android.hardware.usb.UsbManager.USB_CONNECTED;
-import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM;
-import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
-import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
-import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
-import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
-import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
-import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.TetheringManager.EXTRA_ERRORED_TETHER;
-import static android.net.TetheringManager.TETHERING_BLUETOOTH;
-import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringManager.TETHERING_INVALID;
-import static android.net.TetheringManager.TETHERING_NCM;
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHERING_WIGIG;
-import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
-import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
-import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE;
-import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
-import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
-import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
-import static android.net.util.TetheringMessageBase.BASE_MAIN_SM;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
-
-import android.app.usage.NetworkStatsManager;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothPan;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProfile.ServiceListener;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
-import android.net.EthernetManager;
-import android.net.IIntResultListener;
-import android.net.INetd;
-import android.net.ITetheringEventCallback;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.TetherStatesParcel;
-import android.net.TetheredClient;
-import android.net.TetheringCallbackStartedParcel;
-import android.net.TetheringConfigurationParcel;
-import android.net.TetheringRequestParcel;
-import android.net.ip.IpServer;
-import android.net.shared.NetdUtils;
-import android.net.util.BaseNetdUnsolicitedEventListener;
-import android.net.util.InterfaceSet;
-import android.net.util.PrefixUtils;
-import android.net.util.SharedLog;
-import android.net.util.TetheringUtils;
-import android.net.util.VersionedBroadcastListener;
-import android.net.wifi.WifiClient;
-import android.net.wifi.WifiManager;
-import android.net.wifi.p2p.WifiP2pGroup;
-import android.net.wifi.p2p.WifiP2pInfo;
-import android.net.wifi.p2p.WifiP2pManager;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ServiceSpecificException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-
-/**
- *
- * This class holds much of the business logic to allow Android devices
- * to act as IP gateways via USB, BT, and WiFi interfaces.
- */
-public class Tethering {
-
- private static final String TAG = Tethering.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- private static final Class[] sMessageClasses = {
- Tethering.class, TetherMainSM.class, IpServer.class
- };
- private static final SparseArray<String> sMagicDecoderRing =
- MessageUtils.findMessageNames(sMessageClasses);
- // Keep in sync with NETID_UNSET in system/netd/include/netid_client.h
- private static final int NETID_UNSET = 0;
-
- private static class TetherState {
- public final IpServer ipServer;
- public int lastState;
- public int lastError;
-
- TetherState(IpServer ipServer) {
- this.ipServer = ipServer;
- // Assume all state machines start out available and with no errors.
- lastState = IpServer.STATE_AVAILABLE;
- lastError = TETHER_ERROR_NO_ERROR;
- }
-
- public boolean isCurrentlyServing() {
- switch (lastState) {
- case IpServer.STATE_TETHERED:
- case IpServer.STATE_LOCAL_ONLY:
- return true;
- default:
- return false;
- }
- }
- }
-
- /**
- * Cookie added when registering {@link android.net.TetheringManager.TetheringEventCallback}.
- */
- private static class CallbackCookie {
- public final boolean hasListClientsPermission;
-
- private CallbackCookie(boolean hasListClientsPermission) {
- this.hasListClientsPermission = hasListClientsPermission;
- }
- }
-
- private final SharedLog mLog = new SharedLog(TAG);
- private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
- new RemoteCallbackList<>();
- // Currently active tethering requests per tethering type. Only one of each type can be
- // requested at a time. After a tethering type is requested, the map keeps tethering parameters
- // to be used after the interface comes up asynchronously.
- private final SparseArray<TetheringRequestParcel> mActiveTetheringRequests =
- new SparseArray<>();
-
- // used to synchronize public access to members
- // TODO(b/153621704): remove mPublicSync to make Tethering lock free
- private final Object mPublicSync;
- private final Context mContext;
- private final ArrayMap<String, TetherState> mTetherStates;
- private final BroadcastReceiver mStateReceiver;
- private final Looper mLooper;
- private final StateMachine mTetherMainSM;
- private final OffloadController mOffloadController;
- private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
- // TODO: Figure out how to merge this and other downstream-tracking objects
- // into a single coherent structure.
- // Use LinkedHashSet for predictable ordering order for ConnectedClientsTracker.
- private final LinkedHashSet<IpServer> mForwardedDownstreams;
- private final VersionedBroadcastListener mCarrierConfigChange;
- private final TetheringDependencies mDeps;
- private final EntitlementManager mEntitlementMgr;
- private final Handler mHandler;
- private final INetd mNetd;
- private final NetdCallback mNetdCallback;
- private final UserRestrictionActionListener mTetheringRestriction;
- private final ActiveDataSubIdListener mActiveDataSubIdListener;
- private final ConnectedClientsTracker mConnectedClientsTracker;
- private final TetheringThreadExecutor mExecutor;
- private final TetheringNotificationUpdater mNotificationUpdater;
- private final UserManager mUserManager;
- private final BpfCoordinator mBpfCoordinator;
- private final PrivateAddressCoordinator mPrivateAddressCoordinator;
- private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
- // All the usage of mTetheringEventCallback should run in the same thread.
- private ITetheringEventCallback mTetheringEventCallback = null;
-
- private volatile TetheringConfiguration mConfig;
- private InterfaceSet mCurrentUpstreamIfaceSet;
-
- private boolean mRndisEnabled; // track the RNDIS function enabled state
- // True iff. WiFi tethering should be started when soft AP is ready.
- private boolean mWifiTetherRequested;
- private Network mTetherUpstream;
- private TetherStatesParcel mTetherStatesParcel;
- private boolean mDataSaverEnabled = false;
- private String mWifiP2pTetherInterface = null;
- private int mOffloadStatus = TETHER_HARDWARE_OFFLOAD_STOPPED;
-
- @GuardedBy("mPublicSync")
- private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest;
- @GuardedBy("mPublicSync")
- private String mConfiguredEthernetIface;
- @GuardedBy("mPublicSync")
- private EthernetCallback mEthernetCallback;
-
- public Tethering(TetheringDependencies deps) {
- mLog.mark("Tethering.constructed");
- mDeps = deps;
- mContext = mDeps.getContext();
- mNetd = mDeps.getINetd(mContext);
- mLooper = mDeps.getTetheringLooper();
- mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
-
- mPublicSync = new Object();
-
- mTetherStates = new ArrayMap<>();
- mConnectedClientsTracker = new ConnectedClientsTracker();
-
- mTetherMainSM = new TetherMainSM("TetherMain", mLooper, deps);
- mTetherMainSM.start();
-
- mHandler = mTetherMainSM.getHandler();
- mOffloadController = mDeps.getOffloadController(mHandler, mLog,
- new OffloadController.Dependencies() {
-
- @Override
- public TetheringConfiguration getTetherConfig() {
- return mConfig;
- }
- });
- mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMainSM, mLog,
- TetherMainSM.EVENT_UPSTREAM_CALLBACK);
- mForwardedDownstreams = new LinkedHashSet<>();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
- // EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream
- // permission is changed according to entitlement check result.
- mEntitlementMgr = mDeps.getEntitlementManager(mContext, mHandler, mLog,
- () -> mTetherMainSM.sendMessage(
- TetherMainSM.EVENT_UPSTREAM_PERMISSION_CHANGED));
- mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> {
- mLog.log("OBSERVED UiEnitlementFailed");
- stopTethering(downstream);
- });
- mEntitlementMgr.setTetheringConfigurationFetcher(() -> {
- return mConfig;
- });
-
- mCarrierConfigChange = new VersionedBroadcastListener(
- "CarrierConfigChangeListener", mContext, mHandler, filter,
- (Intent ignored) -> {
- mLog.log("OBSERVED carrier config change");
- updateConfiguration();
- mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
- });
-
- mStateReceiver = new StateReceiver();
-
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mTetheringRestriction = new UserRestrictionActionListener(
- mUserManager, this, mNotificationUpdater);
- mExecutor = new TetheringThreadExecutor(mHandler);
- mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
- mNetdCallback = new NetdCallback();
-
- // Load tethering configuration.
- updateConfiguration();
- // It is OK for the configuration to be passed to the PrivateAddressCoordinator at
- // construction time because the only part of the configuration it uses is
- // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that.
- mPrivateAddressCoordinator = mDeps.getPrivateAddressCoordinator(mContext, mConfig);
-
- // Must be initialized after tethering configuration is loaded because BpfCoordinator
- // constructor needs to use the configuration.
- mBpfCoordinator = mDeps.getBpfCoordinator(
- new BpfCoordinator.Dependencies() {
- @NonNull
- public Handler getHandler() {
- return mHandler;
- }
-
- @NonNull
- public INetd getNetd() {
- return mNetd;
- }
-
- @NonNull
- public NetworkStatsManager getNetworkStatsManager() {
- return mContext.getSystemService(NetworkStatsManager.class);
- }
-
- @NonNull
- public SharedLog getSharedLog() {
- return mLog;
- }
-
- @Nullable
- public TetheringConfiguration getTetherConfig() {
- return mConfig;
- }
- });
-
- startStateMachineUpdaters();
- }
-
- /**
- * Start to register callbacks.
- * Call this function when tethering is ready to handle callback events.
- */
- private void startStateMachineUpdaters() {
- try {
- mNetd.registerUnsolicitedEventListener(mNetdCallback);
- } catch (RemoteException e) {
- mLog.e("Unable to register netd UnsolicitedEventListener");
- }
- mCarrierConfigChange.startListening();
- mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener,
- PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(UsbManager.ACTION_USB_STATE);
- filter.addAction(CONNECTIVITY_ACTION);
- filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
- filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
- filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
- mContext.registerReceiver(mStateReceiver, filter, null, mHandler);
-
- final IntentFilter noUpstreamFilter = new IntentFilter();
- noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING);
- mContext.registerReceiver(
- mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler);
-
- final WifiManager wifiManager = getWifiManager();
- if (wifiManager != null) {
- wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
- }
-
- startTrackDefaultNetwork();
- }
-
- private class TetheringThreadExecutor implements Executor {
- private final Handler mTetherHandler;
- TetheringThreadExecutor(Handler handler) {
- mTetherHandler = handler;
- }
- @Override
- public void execute(Runnable command) {
- if (!mTetherHandler.post(command)) {
- throw new RejectedExecutionException(mTetherHandler + " is shutting down");
- }
- }
- }
-
- private class ActiveDataSubIdListener extends PhoneStateListener {
- ActiveDataSubIdListener(Executor executor) {
- super(executor);
- }
-
- @Override
- public void onActiveDataSubscriptionIdChanged(int subId) {
- mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId
- + " to " + subId);
- if (subId == mActiveDataSubId) return;
-
- mActiveDataSubId = subId;
- updateConfiguration();
- mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId);
- // To avoid launching unexpected provisioning checks, ignore re-provisioning
- // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
- // will be triggered again when CarrierConfig is loaded.
- if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
- mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
- } else {
- mLog.log("IGNORED reevaluate provisioning, no carrier config loaded");
- }
- }
- }
-
- private WifiManager getWifiManager() {
- return (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
- }
-
- // NOTE: This is always invoked on the mLooper thread.
- private void updateConfiguration() {
- mConfig = mDeps.generateTetheringConfiguration(mContext, mLog, mActiveDataSubId);
- mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
- reportConfigurationChanged(mConfig.toStableParcelable());
- }
-
- private void maybeDunSettingChanged() {
- final boolean isDunRequired = TetheringConfiguration.checkDunRequired(mContext);
- if (isDunRequired == mConfig.isDunRequired) return;
- updateConfiguration();
- }
-
- private class NetdCallback extends BaseNetdUnsolicitedEventListener {
- @Override
- public void onInterfaceChanged(String ifName, boolean up) {
- mHandler.post(() -> interfaceStatusChanged(ifName, up));
- }
-
- @Override
- public void onInterfaceLinkStateChanged(String ifName, boolean up) {
- mHandler.post(() -> interfaceLinkStateChanged(ifName, up));
- }
-
- @Override
- public void onInterfaceAdded(String ifName) {
- mHandler.post(() -> interfaceAdded(ifName));
- }
-
- @Override
- public void onInterfaceRemoved(String ifName) {
- mHandler.post(() -> interfaceRemoved(ifName));
- }
- }
-
- private class TetheringSoftApCallback implements WifiManager.SoftApCallback {
- // TODO: Remove onStateChanged override when this method has default on
- // WifiManager#SoftApCallback interface.
- // Wifi listener for state change of the soft AP
- @Override
- public void onStateChanged(final int state, final int failureReason) {
- // Nothing
- }
-
- // Called by wifi when the number of soft AP clients changed.
- @Override
- public void onConnectedClientsChanged(final List<WifiClient> clients) {
- updateConnectedClients(clients);
- }
- }
-
- void interfaceStatusChanged(String iface, boolean up) {
- // Never called directly: only called from interfaceLinkStateChanged.
- // See NetlinkHandler.cpp: notifyInterfaceChanged.
- if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
- synchronized (mPublicSync) {
- if (up) {
- maybeTrackNewInterfaceLocked(iface);
- } else {
- if (ifaceNameToType(iface) == TETHERING_BLUETOOTH
- || ifaceNameToType(iface) == TETHERING_WIGIG) {
- stopTrackingInterfaceLocked(iface);
- } else {
- // Ignore usb0 down after enabling RNDIS.
- // We will handle disconnect in interfaceRemoved.
- // Similarly, ignore interface down for WiFi. We monitor WiFi AP status
- // through the WifiManager.WIFI_AP_STATE_CHANGED_ACTION intent.
- if (VDBG) Log.d(TAG, "ignore interface down for " + iface);
- }
- }
- }
- }
-
- void interfaceLinkStateChanged(String iface, boolean up) {
- interfaceStatusChanged(iface, up);
- }
-
- private int ifaceNameToType(String iface) {
- final TetheringConfiguration cfg = mConfig;
-
- if (cfg.isWifi(iface)) {
- return TETHERING_WIFI;
- } else if (cfg.isWigig(iface)) {
- return TETHERING_WIGIG;
- } else if (cfg.isWifiP2p(iface)) {
- return TETHERING_WIFI_P2P;
- } else if (cfg.isUsb(iface)) {
- return TETHERING_USB;
- } else if (cfg.isBluetooth(iface)) {
- return TETHERING_BLUETOOTH;
- } else if (cfg.isNcm(iface)) {
- return TETHERING_NCM;
- }
- return TETHERING_INVALID;
- }
-
- void interfaceAdded(String iface) {
- if (VDBG) Log.d(TAG, "interfaceAdded " + iface);
- synchronized (mPublicSync) {
- maybeTrackNewInterfaceLocked(iface);
- }
- }
-
- void interfaceRemoved(String iface) {
- if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
- synchronized (mPublicSync) {
- stopTrackingInterfaceLocked(iface);
- }
- }
-
- void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
- mHandler.post(() -> {
- final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(
- request.tetheringType);
- // If tethering is already enabled with a different request,
- // disable before re-enabling.
- if (unfinishedRequest != null
- && !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) {
- enableTetheringInternal(request.tetheringType, false /* disabled */, null);
- mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType);
- }
- mActiveTetheringRequests.put(request.tetheringType, request);
-
- if (request.exemptFromEntitlementCheck) {
- mEntitlementMgr.setExemptedDownstreamType(request.tetheringType);
- } else {
- mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
- request.showProvisioningUi);
- }
- enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
- });
- }
-
- void stopTethering(int type) {
- mHandler.post(() -> {
- mActiveTetheringRequests.remove(type);
-
- enableTetheringInternal(type, false /* disabled */, null);
- mEntitlementMgr.stopProvisioningIfNeeded(type);
- });
- }
-
- /**
- * Enables or disables tethering for the given type. If provisioning is required, it will
- * schedule provisioning rechecks for the specified interface.
- */
- private void enableTetheringInternal(int type, boolean enable,
- final IIntResultListener listener) {
- int result = TETHER_ERROR_NO_ERROR;
- switch (type) {
- case TETHERING_WIFI:
- result = setWifiTethering(enable);
- break;
- case TETHERING_USB:
- result = setUsbTethering(enable);
- break;
- case TETHERING_BLUETOOTH:
- setBluetoothTethering(enable, listener);
- break;
- case TETHERING_NCM:
- result = setNcmTethering(enable);
- break;
- case TETHERING_ETHERNET:
- result = setEthernetTethering(enable);
- break;
- default:
- Log.w(TAG, "Invalid tether type.");
- result = TETHER_ERROR_UNKNOWN_TYPE;
- }
-
- // The result of Bluetooth tethering will be sent by #setBluetoothTethering.
- if (type != TETHERING_BLUETOOTH) {
- sendTetherResult(listener, result, type);
- }
- }
-
- private void sendTetherResult(final IIntResultListener listener, final int result,
- final int type) {
- if (listener != null) {
- try {
- listener.onResult(result);
- } catch (RemoteException e) { }
- }
-
- // If changing tethering fail, remove corresponding request
- // no matter who trigger the start/stop.
- if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type);
- }
-
- private int setWifiTethering(final boolean enable) {
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mPublicSync) {
- final WifiManager mgr = getWifiManager();
- if (mgr == null) {
- mLog.e("setWifiTethering: failed to get WifiManager!");
- return TETHER_ERROR_SERVICE_UNAVAIL;
- }
- if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */))
- || (!enable && mgr.stopSoftAp())) {
- mWifiTetherRequested = enable;
- return TETHER_ERROR_NO_ERROR;
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- return TETHER_ERROR_INTERNAL_ERROR;
- }
-
- private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) {
- final BluetoothAdapter adapter = mDeps.getBluetoothAdapter();
- if (adapter == null || !adapter.isEnabled()) {
- Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
- + (adapter == null));
- sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH);
- return;
- }
-
- adapter.getProfileProxy(mContext, new ServiceListener() {
- @Override
- public void onServiceDisconnected(int profile) { }
-
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- // Clear identify is fine because caller already pass tethering permission at
- // ConnectivityService#startTethering()(or stopTethering) before the control comes
- // here. Bluetooth will check tethering permission again that there is
- // Context#getOpPackageName() under BluetoothPan#setBluetoothTethering() to get
- // caller's package name for permission check.
- // Calling BluetoothPan#setBluetoothTethering() here means the package name always
- // be system server. If calling identity is not cleared, that package's uid might
- // not match calling uid and end up in permission denied.
- final long identityToken = Binder.clearCallingIdentity();
- try {
- ((BluetoothPan) proxy).setBluetoothTethering(enable);
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- // TODO: Enabling bluetooth tethering can fail asynchronously here.
- // We should figure out a way to bubble up that failure instead of sending success.
- final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
- ? TETHER_ERROR_NO_ERROR
- : TETHER_ERROR_INTERNAL_ERROR;
- sendTetherResult(listener, result, TETHERING_BLUETOOTH);
- adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
- }
- }, BluetoothProfile.PAN);
- }
-
- private int setEthernetTethering(final boolean enable) {
- final EthernetManager em = (EthernetManager) mContext.getSystemService(
- Context.ETHERNET_SERVICE);
- synchronized (mPublicSync) {
- if (enable) {
- if (mEthernetCallback != null) {
- Log.d(TAG, "Ethernet tethering already started");
- return TETHER_ERROR_NO_ERROR;
- }
-
- mEthernetCallback = new EthernetCallback();
- mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
- } else {
- stopEthernetTetheringLocked();
- }
- }
- return TETHER_ERROR_NO_ERROR;
- }
-
- private void stopEthernetTetheringLocked() {
- if (mConfiguredEthernetIface != null) {
- stopTrackingInterfaceLocked(mConfiguredEthernetIface);
- mConfiguredEthernetIface = null;
- }
- if (mEthernetCallback != null) {
- mEthernetIfaceRequest.release();
- mEthernetCallback = null;
- mEthernetIfaceRequest = null;
- }
- }
-
- private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback {
- @Override
- public void onAvailable(String iface) {
- synchronized (mPublicSync) {
- if (this != mEthernetCallback) {
- // Ethernet callback arrived after Ethernet tethering stopped. Ignore.
- return;
- }
- maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET);
- changeInterfaceState(iface, IpServer.STATE_TETHERED);
- mConfiguredEthernetIface = iface;
- }
- }
-
- @Override
- public void onUnavailable() {
- synchronized (mPublicSync) {
- if (this != mEthernetCallback) {
- // onAvailable called after stopping Ethernet tethering.
- return;
- }
- stopEthernetTetheringLocked();
- }
- }
- }
-
- int tether(String iface) {
- return tether(iface, IpServer.STATE_TETHERED);
- }
-
- private int tether(String iface, int requestedState) {
- if (DBG) Log.d(TAG, "Tethering " + iface);
- synchronized (mPublicSync) {
- TetherState tetherState = mTetherStates.get(iface);
- if (tetherState == null) {
- Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring");
- return TETHER_ERROR_UNKNOWN_IFACE;
- }
- // Ignore the error status of the interface. If the interface is available,
- // the errors are referring to past tethering attempts anyway.
- if (tetherState.lastState != IpServer.STATE_AVAILABLE) {
- Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
- return TETHER_ERROR_UNAVAIL_IFACE;
- }
- // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet
- // processed, this will be a no-op and it will not return an error.
- //
- // This code cannot race with untether() because they both synchronize on mPublicSync.
- // TODO: reexamine the threading and messaging model to totally remove mPublicSync.
- final int type = tetherState.ipServer.interfaceType();
- final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null);
- if (request != null) {
- mActiveTetheringRequests.delete(type);
- }
- tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0,
- request);
- return TETHER_ERROR_NO_ERROR;
- }
- }
-
- int untether(String iface) {
- if (DBG) Log.d(TAG, "Untethering " + iface);
- synchronized (mPublicSync) {
- TetherState tetherState = mTetherStates.get(iface);
- if (tetherState == null) {
- Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
- return TETHER_ERROR_UNKNOWN_IFACE;
- }
- if (!tetherState.isCurrentlyServing()) {
- Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
- return TETHER_ERROR_UNAVAIL_IFACE;
- }
- tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED);
- return TETHER_ERROR_NO_ERROR;
- }
- }
-
- void untetherAll() {
- stopTethering(TETHERING_WIFI);
- stopTethering(TETHERING_WIFI_P2P);
- stopTethering(TETHERING_USB);
- stopTethering(TETHERING_BLUETOOTH);
- stopTethering(TETHERING_ETHERNET);
- }
-
- int getLastTetherError(String iface) {
- synchronized (mPublicSync) {
- TetherState tetherState = mTetherStates.get(iface);
- if (tetherState == null) {
- Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface
- + ", ignoring");
- return TETHER_ERROR_UNKNOWN_IFACE;
- }
- return tetherState.lastError;
- }
- }
-
- private boolean isProvisioningNeededButUnavailable() {
- return isTetherProvisioningRequired() && !doesEntitlementPackageExist();
- }
-
- boolean isTetherProvisioningRequired() {
- final TetheringConfiguration cfg = mConfig;
- return mEntitlementMgr.isTetherProvisioningRequired(cfg);
- }
-
- private boolean doesEntitlementPackageExist() {
- // provisioningApp must contain package and class name.
- if (mConfig.provisioningApp.length != 2) {
- return false;
- }
-
- final PackageManager pm = mContext.getPackageManager();
- try {
- pm.getPackageInfo(mConfig.provisioningApp[0], GET_ACTIVITIES);
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
- return true;
- }
-
- // TODO: Figure out how to update for local hotspot mode interfaces.
- private void sendTetherStateChangedBroadcast() {
- if (!isTetheringSupported()) return;
-
- final ArrayList<String> availableList = new ArrayList<>();
- final ArrayList<String> tetherList = new ArrayList<>();
- final ArrayList<String> localOnlyList = new ArrayList<>();
- final ArrayList<String> erroredList = new ArrayList<>();
- final ArrayList<Integer> lastErrorList = new ArrayList<>();
-
- final TetheringConfiguration cfg = mConfig;
- mTetherStatesParcel = new TetherStatesParcel();
-
- int downstreamTypesMask = DOWNSTREAM_NONE;
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- String iface = mTetherStates.keyAt(i);
- if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
- erroredList.add(iface);
- lastErrorList.add(tetherState.lastError);
- } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
- availableList.add(iface);
- } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
- localOnlyList.add(iface);
- } else if (tetherState.lastState == IpServer.STATE_TETHERED) {
- if (cfg.isUsb(iface)) {
- downstreamTypesMask |= (1 << TETHERING_USB);
- } else if (cfg.isWifi(iface)) {
- downstreamTypesMask |= (1 << TETHERING_WIFI);
- } else if (cfg.isBluetooth(iface)) {
- downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
- }
- tetherList.add(iface);
- }
- }
- }
-
- mTetherStatesParcel.availableList = availableList.toArray(new String[0]);
- mTetherStatesParcel.tetheredList = tetherList.toArray(new String[0]);
- mTetherStatesParcel.localOnlyList = localOnlyList.toArray(new String[0]);
- mTetherStatesParcel.erroredIfaceList = erroredList.toArray(new String[0]);
- mTetherStatesParcel.lastErrorList = new int[lastErrorList.size()];
- Iterator<Integer> iterator = lastErrorList.iterator();
- for (int i = 0; i < lastErrorList.size(); i++) {
- mTetherStatesParcel.lastErrorList[i] = iterator.next().intValue();
- }
- reportTetherStateChanged(mTetherStatesParcel);
-
- final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
- bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList);
- bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
- bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList);
- bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, erroredList);
- mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL);
- if (DBG) {
- Log.d(TAG, String.format(
- "sendTetherStateChangedBroadcast %s=[%s] %s=[%s] %s=[%s] %s=[%s]",
- "avail", TextUtils.join(",", availableList),
- "local_only", TextUtils.join(",", localOnlyList),
- "tether", TextUtils.join(",", tetherList),
- "error", TextUtils.join(",", erroredList)));
- }
-
- mNotificationUpdater.onDownstreamChanged(downstreamTypesMask);
- }
-
- private class StateReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context content, Intent intent) {
- final String action = intent.getAction();
- if (action == null) return;
-
- if (action.equals(UsbManager.ACTION_USB_STATE)) {
- handleUsbAction(intent);
- } else if (action.equals(CONNECTIVITY_ACTION)) {
- handleConnectivityAction(intent);
- } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
- handleWifiApAction(intent);
- } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
- handleWifiP2pAction(intent);
- } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
- mLog.log("OBSERVED configuration changed");
- updateConfiguration();
- } else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {
- mLog.log("OBSERVED user restrictions changed");
- handleUserRestrictionAction();
- } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {
- mLog.log("OBSERVED data saver changed");
- handleDataSaverChanged();
- } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) {
- untetherAll();
- }
- }
-
- private void handleConnectivityAction(Intent intent) {
- final NetworkInfo networkInfo =
- (NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO);
- if (networkInfo == null
- || networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
- return;
- }
-
- if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION: " + networkInfo.toString());
- mTetherMainSM.sendMessage(TetherMainSM.CMD_UPSTREAM_CHANGED);
- }
-
- private void handleUsbAction(Intent intent) {
- final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false);
- final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false);
- final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
- final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false);
-
- mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s",
- usbConnected, usbConfigured, rndisEnabled));
-
- // There are three types of ACTION_USB_STATE:
- //
- // - DISCONNECTED (USB_CONNECTED and USB_CONFIGURED are 0)
- // Meaning: USB connection has ended either because of
- // software reset or hard unplug.
- //
- // - CONNECTED (USB_CONNECTED is 1, USB_CONFIGURED is 0)
- // Meaning: the first stage of USB protocol handshake has
- // occurred but it is not complete.
- //
- // - CONFIGURED (USB_CONNECTED and USB_CONFIGURED are 1)
- // Meaning: the USB handshake is completely done and all the
- // functions are ready to use.
- //
- // For more explanation, see b/62552150 .
- synchronized (Tethering.this.mPublicSync) {
- if (!usbConnected && mRndisEnabled) {
- // Turn off tethering if it was enabled and there is a disconnect.
- tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
- mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB);
- } else if (usbConfigured && rndisEnabled) {
- // Tether if rndis is enabled and usb is configured.
- tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
- } else if (usbConnected && ncmEnabled) {
- tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM);
- }
- mRndisEnabled = usbConfigured && rndisEnabled;
- }
- }
-
- private void handleWifiApAction(Intent intent) {
- final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
- final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
- final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED);
-
- synchronized (Tethering.this.mPublicSync) {
- switch (curState) {
- case WifiManager.WIFI_AP_STATE_ENABLING:
- // We can see this state on the way to both enabled and failure states.
- break;
- case WifiManager.WIFI_AP_STATE_ENABLED:
- enableWifiIpServingLocked(ifname, ipmode);
- break;
- case WifiManager.WIFI_AP_STATE_DISABLING:
- // We can see this state on the way to disabled.
- break;
- case WifiManager.WIFI_AP_STATE_DISABLED:
- case WifiManager.WIFI_AP_STATE_FAILED:
- default:
- disableWifiIpServingLocked(ifname, curState);
- break;
- }
- }
- }
-
- private boolean isGroupOwner(WifiP2pGroup group) {
- return group != null && group.isGroupOwner()
- && !TextUtils.isEmpty(group.getInterface());
- }
-
- private void handleWifiP2pAction(Intent intent) {
- if (mConfig.isWifiP2pLegacyTetheringMode()) return;
-
- final WifiP2pInfo p2pInfo =
- (WifiP2pInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
- final WifiP2pGroup group =
- (WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
-
- if (VDBG) {
- Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group);
- }
-
- synchronized (Tethering.this.mPublicSync) {
- // if no group is formed, bring it down if needed.
- if (p2pInfo == null || !p2pInfo.groupFormed) {
- disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface);
- mWifiP2pTetherInterface = null;
- return;
- }
-
- // If there is a group but the device is not the owner, bail out.
- if (!isGroupOwner(group)) return;
-
- // If already serving from the correct interface, nothing to do.
- if (group.getInterface().equals(mWifiP2pTetherInterface)) return;
-
- // If already serving from another interface, turn it down first.
- if (!TextUtils.isEmpty(mWifiP2pTetherInterface)) {
- mLog.w("P2P tethered interface " + mWifiP2pTetherInterface
- + "is different from current interface "
- + group.getInterface() + ", re-tether it");
- disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface);
- }
-
- // Finally bring up serving on the new interface
- mWifiP2pTetherInterface = group.getInterface();
- enableWifiIpServingLocked(mWifiP2pTetherInterface, IFACE_IP_MODE_LOCAL_ONLY);
- }
- }
-
- private void handleUserRestrictionAction() {
- mTetheringRestriction.onUserRestrictionsChanged();
- }
-
- private void handleDataSaverChanged() {
- final ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- final boolean isDataSaverEnabled = connMgr.getRestrictBackgroundStatus()
- != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
-
- if (mDataSaverEnabled == isDataSaverEnabled) return;
-
- mDataSaverEnabled = isDataSaverEnabled;
- if (mDataSaverEnabled) {
- untetherAll();
- }
- }
- }
-
- @VisibleForTesting
- boolean isTetheringActive() {
- return mActiveTetheringRequests.size() > 0;
- }
-
- @VisibleForTesting
- protected static class UserRestrictionActionListener {
- private final UserManager mUserMgr;
- private final Tethering mWrapper;
- private final TetheringNotificationUpdater mNotificationUpdater;
- public boolean mDisallowTethering;
-
- public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
- @NonNull TetheringNotificationUpdater updater) {
- mUserMgr = um;
- mWrapper = wrapper;
- mNotificationUpdater = updater;
- mDisallowTethering = false;
- }
-
- public void onUserRestrictionsChanged() {
- // getUserRestrictions gets restriction for this process' user, which is the primary
- // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary
- // user. See UserManager.DISALLOW_CONFIG_TETHERING.
- final Bundle restrictions = mUserMgr.getUserRestrictions();
- final boolean newlyDisallowed =
- restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
- final boolean prevDisallowed = mDisallowTethering;
- mDisallowTethering = newlyDisallowed;
-
- final boolean tetheringDisallowedChanged = (newlyDisallowed != prevDisallowed);
- if (!tetheringDisallowedChanged) {
- return;
- }
-
- if (!newlyDisallowed) {
- // Clear the restricted notification when user is allowed to have tethering
- // function.
- mNotificationUpdater.tetheringRestrictionLifted();
- return;
- }
-
- if (mWrapper.isTetheringActive()) {
- // Restricted notification is shown when tethering function is disallowed on
- // user's device.
- mNotificationUpdater.notifyTetheringDisabledByRestriction();
-
- // Untether from all downstreams since tethering is disallowed.
- mWrapper.untetherAll();
- }
- // TODO(b/148139325): send tetheringSupported on restriction change
- }
- }
-
- private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) {
- mLog.log("Canceling WiFi tethering request -"
- + " type=" + tetheringType
- + " interface=" + ifname
- + " state=" + apState);
-
- if (!TextUtils.isEmpty(ifname)) {
- final TetherState ts = mTetherStates.get(ifname);
- if (ts != null) {
- ts.ipServer.unwanted();
- return;
- }
- }
-
- for (int i = 0; i < mTetherStates.size(); i++) {
- final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
- if (ipServer.interfaceType() == tetheringType) {
- ipServer.unwanted();
- return;
- }
- }
-
- mLog.log("Error disabling Wi-Fi IP serving; "
- + (TextUtils.isEmpty(ifname) ? "no interface name specified"
- : "specified interface: " + ifname));
- }
-
- private void disableWifiIpServingLocked(String ifname, int apState) {
- // Regardless of whether we requested this transition, the AP has gone
- // down. Don't try to tether again unless we're requested to do so.
- // TODO: Remove this altogether, once Wi-Fi reliably gives us an
- // interface name with every broadcast.
- mWifiTetherRequested = false;
-
- disableWifiIpServingLockedCommon(TETHERING_WIFI, ifname, apState);
- }
-
- private void disableWifiP2pIpServingLockedIfNeeded(String ifname) {
- if (TextUtils.isEmpty(ifname)) return;
-
- disableWifiIpServingLockedCommon(TETHERING_WIFI_P2P, ifname, /* fake */ 0);
- }
-
- private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
- // Map wifiIpMode values to IpServer.Callback serving states, inferring
- // from mWifiTetherRequested as a final "best guess".
- final int ipServingMode;
- switch (wifiIpMode) {
- case IFACE_IP_MODE_TETHERED:
- ipServingMode = IpServer.STATE_TETHERED;
- break;
- case IFACE_IP_MODE_LOCAL_ONLY:
- ipServingMode = IpServer.STATE_LOCAL_ONLY;
- break;
- default:
- mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode);
- return;
- }
-
- if (!TextUtils.isEmpty(ifname)) {
- maybeTrackNewInterfaceLocked(ifname);
- changeInterfaceState(ifname, ipServingMode);
- } else {
- mLog.e(String.format(
- "Cannot enable IP serving in mode %s on missing interface name",
- ipServingMode));
- }
- }
-
- // TODO: Consider renaming to something more accurate in its description.
- // This method:
- // - allows requesting either tethering or local hotspot serving states
- // - handles both enabling and disabling serving states
- // - only tethers the first matching interface in listInterfaces()
- // order of a given type
- private void tetherMatchingInterfaces(int requestedState, int interfaceType) {
- if (VDBG) {
- Log.d(TAG, "tetherMatchingInterfaces(" + requestedState + ", " + interfaceType + ")");
- }
-
- String[] ifaces = null;
- try {
- ifaces = mNetd.interfaceGetList();
- } catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "Error listing Interfaces", e);
- return;
- }
- String chosenIface = null;
- if (ifaces != null) {
- for (String iface : ifaces) {
- if (ifaceNameToType(iface) == interfaceType) {
- chosenIface = iface;
- break;
- }
- }
- }
- if (chosenIface == null) {
- Log.e(TAG, "could not find iface of type " + interfaceType);
- return;
- }
-
- changeInterfaceState(chosenIface, requestedState);
- }
-
- private void changeInterfaceState(String ifname, int requestedState) {
- final int result;
- switch (requestedState) {
- case IpServer.STATE_UNAVAILABLE:
- case IpServer.STATE_AVAILABLE:
- result = untether(ifname);
- break;
- case IpServer.STATE_TETHERED:
- case IpServer.STATE_LOCAL_ONLY:
- result = tether(ifname, requestedState);
- break;
- default:
- Log.wtf(TAG, "Unknown interface state: " + requestedState);
- return;
- }
- if (result != TETHER_ERROR_NO_ERROR) {
- Log.e(TAG, "unable start or stop tethering on iface " + ifname);
- return;
- }
- }
-
- TetheringConfiguration getTetheringConfiguration() {
- return mConfig;
- }
-
- boolean hasTetherableConfiguration() {
- final TetheringConfiguration cfg = mConfig;
- final boolean hasDownstreamConfiguration =
- (cfg.tetherableUsbRegexs.length != 0)
- || (cfg.tetherableWifiRegexs.length != 0)
- || (cfg.tetherableBluetoothRegexs.length != 0);
- final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty()
- || cfg.chooseUpstreamAutomatically;
-
- return hasDownstreamConfiguration && hasUpstreamConfiguration;
- }
-
- // TODO - update callers to use getTetheringConfiguration(),
- // which has only final members.
- String[] getTetherableUsbRegexs() {
- return copy(mConfig.tetherableUsbRegexs);
- }
-
- String[] getTetherableWifiRegexs() {
- return copy(mConfig.tetherableWifiRegexs);
- }
-
- String[] getTetherableBluetoothRegexs() {
- return copy(mConfig.tetherableBluetoothRegexs);
- }
-
- int setUsbTethering(boolean enable) {
- if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
- UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
- if (usbManager == null) {
- mLog.e("setUsbTethering: failed to get UsbManager!");
- return TETHER_ERROR_SERVICE_UNAVAIL;
- }
-
- synchronized (mPublicSync) {
- usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
- : UsbManager.FUNCTION_NONE);
- }
- return TETHER_ERROR_NO_ERROR;
- }
-
- private int setNcmTethering(boolean enable) {
- if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")");
- UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
- synchronized (mPublicSync) {
- usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM
- : UsbManager.FUNCTION_NONE);
- }
- return TETHER_ERROR_NO_ERROR;
- }
-
- // TODO review API - figure out how to delete these entirely.
- String[] getTetheredIfaces() {
- ArrayList<String> list = new ArrayList<String>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState == IpServer.STATE_TETHERED) {
- list.add(mTetherStates.keyAt(i));
- }
- }
- }
- return list.toArray(new String[list.size()]);
- }
-
- String[] getTetherableIfaces() {
- ArrayList<String> list = new ArrayList<String>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
- list.add(mTetherStates.keyAt(i));
- }
- }
- }
- return list.toArray(new String[list.size()]);
- }
-
- String[] getTetheredDhcpRanges() {
- // TODO: this is only valid for the old DHCP server. Latest search suggests it is only used
- // by WifiP2pServiceImpl to start dnsmasq: remove/deprecate after migrating callers.
- return mConfig.legacyDhcpRanges;
- }
-
- String[] getErroredIfaces() {
- ArrayList<String> list = new ArrayList<String>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
- list.add(mTetherStates.keyAt(i));
- }
- }
- }
- return list.toArray(new String[list.size()]);
- }
-
- private void logMessage(State state, int what) {
- mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
- }
-
- private boolean upstreamWanted() {
- if (!mForwardedDownstreams.isEmpty()) return true;
-
- synchronized (mPublicSync) {
- return mWifiTetherRequested;
- }
- }
-
- // Needed because the canonical source of upstream truth is just the
- // upstream interface set, |mCurrentUpstreamIfaceSet|.
- private boolean pertainsToCurrentUpstream(UpstreamNetworkState ns) {
- if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) {
- for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
- if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) {
- return true;
- }
- }
- }
- return false;
- }
-
- class TetherMainSM extends StateMachine {
- // an interface SM has requested Tethering/Local Hotspot
- static final int EVENT_IFACE_SERVING_STATE_ACTIVE = BASE_MAIN_SM + 1;
- // an interface SM has unrequested Tethering/Local Hotspot
- static final int EVENT_IFACE_SERVING_STATE_INACTIVE = BASE_MAIN_SM + 2;
- // upstream connection change - do the right thing
- static final int CMD_UPSTREAM_CHANGED = BASE_MAIN_SM + 3;
- // we don't have a valid upstream conn, check again after a delay
- static final int CMD_RETRY_UPSTREAM = BASE_MAIN_SM + 4;
- // Events from NetworkCallbacks that we process on the main state
- // machine thread on behalf of the UpstreamNetworkMonitor.
- static final int EVENT_UPSTREAM_CALLBACK = BASE_MAIN_SM + 5;
- // we treated the error and want now to clear it
- static final int CMD_CLEAR_ERROR = BASE_MAIN_SM + 6;
- static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MAIN_SM + 7;
- // Events from EntitlementManager to choose upstream again.
- static final int EVENT_UPSTREAM_PERMISSION_CHANGED = BASE_MAIN_SM + 8;
- private final State mInitialState;
- private final State mTetherModeAliveState;
-
- private final State mSetIpForwardingEnabledErrorState;
- private final State mSetIpForwardingDisabledErrorState;
- private final State mStartTetheringErrorState;
- private final State mStopTetheringErrorState;
- private final State mSetDnsForwardersErrorState;
-
- // This list is a little subtle. It contains all the interfaces that currently are
- // requesting tethering, regardless of whether these interfaces are still members of
- // mTetherStates. This allows us to maintain the following predicates:
- //
- // 1) mTetherStates contains the set of all currently existing, tetherable, link state up
- // interfaces.
- // 2) mNotifyList contains all state machines that may have outstanding tethering state
- // that needs to be torn down.
- //
- // Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList
- // so that the garbage collector does not clean up the state machine before it has a chance
- // to tear itself down.
- private final ArrayList<IpServer> mNotifyList;
- private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
- private final OffloadWrapper mOffload;
-
- private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
-
- TetherMainSM(String name, Looper looper, TetheringDependencies deps) {
- super(name, looper);
-
- mInitialState = new InitialState();
- mTetherModeAliveState = new TetherModeAliveState();
- mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState();
- mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState();
- mStartTetheringErrorState = new StartTetheringErrorState();
- mStopTetheringErrorState = new StopTetheringErrorState();
- mSetDnsForwardersErrorState = new SetDnsForwardersErrorState();
-
- addState(mInitialState);
- addState(mTetherModeAliveState);
- addState(mSetIpForwardingEnabledErrorState);
- addState(mSetIpForwardingDisabledErrorState);
- addState(mStartTetheringErrorState);
- addState(mStopTetheringErrorState);
- addState(mSetDnsForwardersErrorState);
-
- mNotifyList = new ArrayList<>();
- mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
- mOffload = new OffloadWrapper();
-
- setInitialState(mInitialState);
- }
-
- class InitialState extends State {
- @Override
- public boolean processMessage(Message message) {
- logMessage(this, message.what);
- switch (message.what) {
- case EVENT_IFACE_SERVING_STATE_ACTIVE: {
- final IpServer who = (IpServer) message.obj;
- if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
- handleInterfaceServingStateActive(message.arg1, who);
- transitionTo(mTetherModeAliveState);
- break;
- }
- case EVENT_IFACE_SERVING_STATE_INACTIVE: {
- final IpServer who = (IpServer) message.obj;
- if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
- handleInterfaceServingStateInactive(who);
- break;
- }
- case EVENT_IFACE_UPDATE_LINKPROPERTIES:
- // Silently ignore these for now.
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- protected boolean turnOnMainTetherSettings() {
- final TetheringConfiguration cfg = mConfig;
- try {
- mNetd.ipfwdEnableForwarding(TAG);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e(e);
- transitionTo(mSetIpForwardingEnabledErrorState);
- return false;
- }
-
- // TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
- // Legacy DHCP server is disabled if passed an empty ranges array
- final String[] dhcpRanges = cfg.enableLegacyDhcpServer
- ? cfg.legacyDhcpRanges : new String[0];
- try {
- NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges);
- } catch (RemoteException | ServiceSpecificException e) {
- try {
- // Stop and retry.
- mNetd.tetherStop();
- NetdUtils.tetherStart(mNetd, true /** usingLegacyDnsProxy */, dhcpRanges);
- } catch (RemoteException | ServiceSpecificException ee) {
- mLog.e(ee);
- transitionTo(mStartTetheringErrorState);
- return false;
- }
- }
- mLog.log("SET main tether settings: ON");
- return true;
- }
-
- protected boolean turnOffMainTetherSettings() {
- try {
- mNetd.tetherStop();
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e(e);
- transitionTo(mStopTetheringErrorState);
- return false;
- }
- try {
- mNetd.ipfwdDisableForwarding(TAG);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e(e);
- transitionTo(mSetIpForwardingDisabledErrorState);
- return false;
- }
- transitionTo(mInitialState);
- mLog.log("SET main tether settings: OFF");
- return true;
- }
-
- protected void chooseUpstreamType(boolean tryCell) {
- // We rebuild configuration on ACTION_CONFIGURATION_CHANGED, but we
- // do not currently know how to watch for changes in DUN settings.
- maybeDunSettingChanged();
-
- final TetheringConfiguration config = mConfig;
- final UpstreamNetworkState ns = (config.chooseUpstreamAutomatically)
- ? mUpstreamNetworkMonitor.getCurrentPreferredUpstream()
- : mUpstreamNetworkMonitor.selectPreferredUpstreamType(
- config.preferredUpstreamIfaceTypes);
- if (ns == null) {
- if (tryCell) {
- mUpstreamNetworkMonitor.registerMobileNetworkRequest();
- // We think mobile should be coming up; don't set a retry.
- } else {
- sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
- }
- }
- setUpstreamNetwork(ns);
- final Network newUpstream = (ns != null) ? ns.network : null;
- if (mTetherUpstream != newUpstream) {
- mTetherUpstream = newUpstream;
- mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream);
- reportUpstreamChanged(ns);
- }
- }
-
- protected void setUpstreamNetwork(UpstreamNetworkState ns) {
- InterfaceSet ifaces = null;
- if (ns != null) {
- // Find the interface with the default IPv4 route. It may be the
- // interface described by linkProperties, or one of the interfaces
- // stacked on top of it.
- mLog.i("Looking for default routes on: " + ns.linkProperties);
- ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns);
- mLog.i("Found upstream interface(s): " + ifaces);
- }
-
- if (ifaces != null) {
- setDnsForwarders(ns.network, ns.linkProperties);
- }
- notifyDownstreamsOfNewUpstreamIface(ifaces);
- if (ns != null && pertainsToCurrentUpstream(ns)) {
- // If we already have UpstreamNetworkState for this network update it immediately.
- handleNewUpstreamNetworkState(ns);
- } else if (mCurrentUpstreamIfaceSet == null) {
- // There are no available upstream networks.
- handleNewUpstreamNetworkState(null);
- }
- }
-
- protected void setDnsForwarders(final Network network, final LinkProperties lp) {
- // TODO: Set v4 and/or v6 DNS per available connectivity.
- final Collection<InetAddress> dnses = lp.getDnsServers();
- // TODO: Properly support the absence of DNS servers.
- final String[] dnsServers;
- if (dnses != null && !dnses.isEmpty()) {
- dnsServers = new String[dnses.size()];
- int i = 0;
- for (InetAddress dns : dnses) {
- dnsServers[i++] = dns.getHostAddress();
- }
- } else {
- dnsServers = mConfig.defaultIPv4DNS;
- }
- final int netId = (network != null) ? network.getNetId() : NETID_UNSET;
- try {
- mNetd.tetherDnsSet(netId, dnsServers);
- mLog.log(String.format(
- "SET DNS forwarders: network=%s dnsServers=%s",
- network, Arrays.toString(dnsServers)));
- } catch (RemoteException | ServiceSpecificException e) {
- // TODO: Investigate how this can fail and what exactly
- // happens if/when such failures occur.
- mLog.e("setting DNS forwarders failed, " + e);
- transitionTo(mSetDnsForwardersErrorState);
- }
- }
-
- protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
- mCurrentUpstreamIfaceSet = ifaces;
- for (IpServer ipServer : mNotifyList) {
- ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces);
- }
- }
-
- protected void handleNewUpstreamNetworkState(UpstreamNetworkState ns) {
- mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
- mOffload.updateUpstreamNetworkState(ns);
- }
-
- private void handleInterfaceServingStateActive(int mode, IpServer who) {
- if (mNotifyList.indexOf(who) < 0) {
- mNotifyList.add(who);
- mIPv6TetheringCoordinator.addActiveDownstream(who, mode);
- }
-
- if (mode == IpServer.STATE_TETHERED) {
- // No need to notify OffloadController just yet as there are no
- // "offload-able" prefixes to pass along. This will handled
- // when the TISM informs Tethering of its LinkProperties.
- mForwardedDownstreams.add(who);
- } else {
- mOffload.excludeDownstreamInterface(who.interfaceName());
- mForwardedDownstreams.remove(who);
- }
-
- // If this is a Wi-Fi interface, notify WifiManager of the active serving state.
- if (who.interfaceType() == TETHERING_WIFI) {
- final WifiManager mgr = getWifiManager();
- final String iface = who.interfaceName();
- switch (mode) {
- case IpServer.STATE_TETHERED:
- mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED);
- break;
- case IpServer.STATE_LOCAL_ONLY:
- mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY);
- break;
- default:
- Log.wtf(TAG, "Unknown active serving mode: " + mode);
- break;
- }
- }
- }
-
- private void handleInterfaceServingStateInactive(IpServer who) {
- mNotifyList.remove(who);
- mIPv6TetheringCoordinator.removeActiveDownstream(who);
- mOffload.excludeDownstreamInterface(who.interfaceName());
- mForwardedDownstreams.remove(who);
- updateConnectedClients(null /* wifiClients */);
-
- // If this is a Wi-Fi interface, tell WifiManager of any errors
- // or the inactive serving state.
- if (who.interfaceType() == TETHERING_WIFI) {
- if (who.lastError() != TETHER_ERROR_NO_ERROR) {
- getWifiManager().updateInterfaceIpState(
- who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR);
- } else {
- getWifiManager().updateInterfaceIpState(
- who.interfaceName(), IFACE_IP_MODE_UNSPECIFIED);
- }
- }
- }
-
- @VisibleForTesting
- void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
- if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
- mOffload.sendOffloadExemptPrefixes((Set<IpPrefix>) o);
- return;
- }
-
- final UpstreamNetworkState ns = (UpstreamNetworkState) o;
- switch (arg1) {
- case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
- mPrivateAddressCoordinator.updateUpstreamPrefix(ns);
- break;
- case UpstreamNetworkMonitor.EVENT_ON_LOST:
- mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network);
- break;
- }
-
- if (ns == null || !pertainsToCurrentUpstream(ns)) {
- // TODO: In future, this is where upstream evaluation and selection
- // could be handled for notifications which include sufficient data.
- // For example, after CONNECTIVITY_ACTION listening is removed, here
- // is where we could observe a Wi-Fi network becoming available and
- // passing validation.
- if (mCurrentUpstreamIfaceSet == null) {
- // If we have no upstream interface, try to run through upstream
- // selection again. If, for example, IPv4 connectivity has shown up
- // after IPv6 (e.g., 464xlat became available) we want the chance to
- // notice and act accordingly.
- chooseUpstreamType(false);
- }
- return;
- }
-
- switch (arg1) {
- case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
- if (ns.network.equals(mTetherUpstream)) {
- mNotificationUpdater.onUpstreamCapabilitiesChanged(ns.networkCapabilities);
- }
- handleNewUpstreamNetworkState(ns);
- break;
- case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
- chooseUpstreamType(false);
- break;
- case UpstreamNetworkMonitor.EVENT_ON_LOST:
- // TODO: Re-evaluate possible upstreams. Currently upstream
- // reevaluation is triggered via received CONNECTIVITY_ACTION
- // broadcasts that result in being passed a
- // TetherMainSM.CMD_UPSTREAM_CHANGED.
- handleNewUpstreamNetworkState(null);
- break;
- default:
- mLog.e("Unknown arg1 value: " + arg1);
- break;
- }
- }
-
- class TetherModeAliveState extends State {
- boolean mUpstreamWanted = false;
- boolean mTryCell = true;
-
- @Override
- public void enter() {
- // If turning on main tether settings fails, we have already
- // transitioned to an error state; exit early.
- if (!turnOnMainTetherSettings()) {
- return;
- }
-
- mPrivateAddressCoordinator.maybeRemoveDeprecatedUpstreams();
- mUpstreamNetworkMonitor.startObserveAllNetworks();
-
- // TODO: De-duplicate with updateUpstreamWanted() below.
- if (upstreamWanted()) {
- mUpstreamWanted = true;
- mOffload.start();
- chooseUpstreamType(true);
- mTryCell = false;
- }
-
- // TODO: Check the upstream interface if it is managed by BPF offload.
- mBpfCoordinator.startPolling();
- }
-
- @Override
- public void exit() {
- mOffload.stop();
- mUpstreamNetworkMonitor.stop();
- notifyDownstreamsOfNewUpstreamIface(null);
- handleNewUpstreamNetworkState(null);
- if (mTetherUpstream != null) {
- mTetherUpstream = null;
- reportUpstreamChanged(null);
- }
- mBpfCoordinator.stopPolling();
- }
-
- private boolean updateUpstreamWanted() {
- final boolean previousUpstreamWanted = mUpstreamWanted;
- mUpstreamWanted = upstreamWanted();
- if (mUpstreamWanted != previousUpstreamWanted) {
- if (mUpstreamWanted) {
- mOffload.start();
- } else {
- mOffload.stop();
- }
- }
- return previousUpstreamWanted;
- }
-
- @Override
- public boolean processMessage(Message message) {
- logMessage(this, message.what);
- boolean retValue = true;
- switch (message.what) {
- case EVENT_IFACE_SERVING_STATE_ACTIVE: {
- IpServer who = (IpServer) message.obj;
- if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
- handleInterfaceServingStateActive(message.arg1, who);
- who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
- mCurrentUpstreamIfaceSet);
- // If there has been a change and an upstream is now
- // desired, kick off the selection process.
- final boolean previousUpstreamWanted = updateUpstreamWanted();
- if (!previousUpstreamWanted && mUpstreamWanted) {
- chooseUpstreamType(true);
- }
- break;
- }
- case EVENT_IFACE_SERVING_STATE_INACTIVE: {
- IpServer who = (IpServer) message.obj;
- if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
- handleInterfaceServingStateInactive(who);
-
- if (mNotifyList.isEmpty()) {
- // This transitions us out of TetherModeAliveState,
- // either to InitialState or an error state.
- turnOffMainTetherSettings();
- break;
- }
-
- if (DBG) {
- Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size()
- + " live requests:");
- for (IpServer o : mNotifyList) {
- Log.d(TAG, " " + o);
- }
- }
- // If there has been a change and an upstream is no
- // longer desired, release any mobile requests.
- final boolean previousUpstreamWanted = updateUpstreamWanted();
- if (previousUpstreamWanted && !mUpstreamWanted) {
- mUpstreamNetworkMonitor.releaseMobileNetworkRequest();
- }
- break;
- }
- case EVENT_IFACE_UPDATE_LINKPROPERTIES: {
- final LinkProperties newLp = (LinkProperties) message.obj;
- if (message.arg1 == IpServer.STATE_TETHERED) {
- mOffload.updateDownstreamLinkProperties(newLp);
- } else {
- mOffload.excludeDownstreamInterface(newLp.getInterfaceName());
- }
- break;
- }
- case EVENT_UPSTREAM_PERMISSION_CHANGED:
- case CMD_UPSTREAM_CHANGED:
- updateUpstreamWanted();
- if (!mUpstreamWanted) break;
-
- // Need to try DUN immediately if Wi-Fi goes down.
- chooseUpstreamType(true);
- mTryCell = false;
- break;
- case CMD_RETRY_UPSTREAM:
- updateUpstreamWanted();
- if (!mUpstreamWanted) break;
-
- chooseUpstreamType(mTryCell);
- mTryCell = !mTryCell;
- break;
- case EVENT_UPSTREAM_CALLBACK: {
- updateUpstreamWanted();
- if (mUpstreamWanted) {
- handleUpstreamNetworkMonitorCallback(message.arg1, message.obj);
- }
- break;
- }
- default:
- retValue = false;
- break;
- }
- return retValue;
- }
- }
-
- class ErrorState extends State {
- private int mErrorNotification;
-
- @Override
- public boolean processMessage(Message message) {
- boolean retValue = true;
- switch (message.what) {
- case EVENT_IFACE_SERVING_STATE_ACTIVE:
- IpServer who = (IpServer) message.obj;
- who.sendMessage(mErrorNotification);
- break;
- case CMD_CLEAR_ERROR:
- mErrorNotification = TETHER_ERROR_NO_ERROR;
- transitionTo(mInitialState);
- break;
- default:
- retValue = false;
- }
- return retValue;
- }
-
- void notify(int msgType) {
- mErrorNotification = msgType;
- for (IpServer ipServer : mNotifyList) {
- ipServer.sendMessage(msgType);
- }
- }
-
- }
-
- class SetIpForwardingEnabledErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error in setIpForwardingEnabled");
- notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR);
- }
- }
-
- class SetIpForwardingDisabledErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error in setIpForwardingDisabled");
- notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR);
- }
- }
-
- class StartTetheringErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error in startTethering");
- notify(IpServer.CMD_START_TETHERING_ERROR);
- try {
- mNetd.ipfwdDisableForwarding(TAG);
- } catch (RemoteException | ServiceSpecificException e) { }
- }
- }
-
- class StopTetheringErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error in stopTethering");
- notify(IpServer.CMD_STOP_TETHERING_ERROR);
- try {
- mNetd.ipfwdDisableForwarding(TAG);
- } catch (RemoteException | ServiceSpecificException e) { }
- }
- }
-
- class SetDnsForwardersErrorState extends ErrorState {
- @Override
- public void enter() {
- Log.e(TAG, "Error in setDnsForwarders");
- notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR);
- try {
- mNetd.tetherStop();
- } catch (RemoteException | ServiceSpecificException e) { }
- try {
- mNetd.ipfwdDisableForwarding(TAG);
- } catch (RemoteException | ServiceSpecificException e) { }
- }
- }
-
- // A wrapper class to handle multiple situations where several calls to
- // the OffloadController need to happen together.
- //
- // TODO: This suggests that the interface between OffloadController and
- // Tethering is in need of improvement. Refactor these calls into the
- // OffloadController implementation.
- class OffloadWrapper {
- public void start() {
- final int status = mOffloadController.start() ? TETHER_HARDWARE_OFFLOAD_STARTED
- : TETHER_HARDWARE_OFFLOAD_FAILED;
- updateOffloadStatus(status);
- sendOffloadExemptPrefixes();
- }
-
- public void stop() {
- mOffloadController.stop();
- updateOffloadStatus(TETHER_HARDWARE_OFFLOAD_STOPPED);
- }
-
- public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
- mOffloadController.setUpstreamLinkProperties(
- (ns != null) ? ns.linkProperties : null);
- }
-
- public void updateDownstreamLinkProperties(LinkProperties newLp) {
- // Update the list of offload-exempt prefixes before adding
- // new prefixes on downstream interfaces to the offload HAL.
- sendOffloadExemptPrefixes();
- mOffloadController.notifyDownstreamLinkProperties(newLp);
- }
-
- public void excludeDownstreamInterface(String ifname) {
- // This and other interfaces may be in local-only hotspot mode;
- // resend all local prefixes to the OffloadController.
- sendOffloadExemptPrefixes();
- mOffloadController.removeDownstreamInterface(ifname);
- }
-
- public void sendOffloadExemptPrefixes() {
- sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes());
- }
-
- public void sendOffloadExemptPrefixes(final Set<IpPrefix> localPrefixes) {
- // Add in well-known minimum set.
- PrefixUtils.addNonForwardablePrefixes(localPrefixes);
- // Add tragically hardcoded prefixes.
- localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX);
-
- // Maybe add prefixes or addresses for downstreams, depending on
- // the IP serving mode of each.
- for (IpServer ipServer : mNotifyList) {
- final LinkProperties lp = ipServer.linkProperties();
-
- switch (ipServer.servingMode()) {
- case IpServer.STATE_UNAVAILABLE:
- case IpServer.STATE_AVAILABLE:
- // No usable LinkProperties in these states.
- continue;
- case IpServer.STATE_TETHERED:
- // Only add IPv4 /32 and IPv6 /128 prefixes. The
- // directly-connected prefixes will be sent as
- // downstream "offload-able" prefixes.
- for (LinkAddress addr : lp.getAllLinkAddresses()) {
- final InetAddress ip = addr.getAddress();
- if (ip.isLinkLocalAddress()) continue;
- localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip));
- }
- break;
- case IpServer.STATE_LOCAL_ONLY:
- // Add prefixes covering all local IPs.
- localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp));
- break;
- }
- }
-
- mOffloadController.setLocalPrefixes(localPrefixes);
- }
-
- private void updateOffloadStatus(final int newStatus) {
- if (newStatus == mOffloadStatus) return;
-
- mOffloadStatus = newStatus;
- reportOffloadStatusChanged(mOffloadStatus);
- }
- }
- }
-
- private void startTrackDefaultNetwork() {
- mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest(),
- mEntitlementMgr);
- }
-
- /** Get the latest value of the tethering entitlement check. */
- void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
- boolean showEntitlementUi) {
- if (receiver == null) return;
-
- mHandler.post(() -> {
- mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver,
- showEntitlementUi);
- });
- }
-
- /** Register tethering event callback */
- void registerTetheringEventCallback(ITetheringEventCallback callback) {
- final boolean hasListPermission =
- hasCallingPermission(NETWORK_SETTINGS)
- || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
- || hasCallingPermission(NETWORK_STACK);
- mHandler.post(() -> {
- mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
- final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
- parcel.tetheringSupported = isTetheringSupported();
- parcel.upstreamNetwork = mTetherUpstream;
- parcel.config = mConfig.toStableParcelable();
- parcel.states =
- mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel();
- parcel.tetheredClients = hasListPermission
- ? mConnectedClientsTracker.getLastTetheredClients()
- : Collections.emptyList();
- parcel.offloadStatus = mOffloadStatus;
- try {
- callback.onCallbackStarted(parcel);
- } catch (RemoteException e) {
- // Not really very much to do here.
- }
- });
- }
-
- private TetherStatesParcel emptyTetherStatesParcel() {
- final TetherStatesParcel parcel = new TetherStatesParcel();
- parcel.availableList = new String[0];
- parcel.tetheredList = new String[0];
- parcel.localOnlyList = new String[0];
- parcel.erroredIfaceList = new String[0];
- parcel.lastErrorList = new int[0];
-
- return parcel;
- }
-
- private boolean hasCallingPermission(@NonNull String permission) {
- return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
- }
-
- /** Unregister tethering event callback */
- void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
- mHandler.post(() -> {
- mTetheringEventCallbacks.unregister(callback);
- });
- }
-
- private void reportUpstreamChanged(UpstreamNetworkState ns) {
- final int length = mTetheringEventCallbacks.beginBroadcast();
- final Network network = (ns != null) ? ns.network : null;
- final NetworkCapabilities capabilities = (ns != null) ? ns.networkCapabilities : null;
- try {
- for (int i = 0; i < length; i++) {
- try {
- mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network);
- } catch (RemoteException e) {
- // Not really very much to do here.
- }
- }
- } finally {
- mTetheringEventCallbacks.finishBroadcast();
- }
- // Need to notify capabilities change after upstream network changed because new network's
- // capabilities should be checked every time.
- mNotificationUpdater.onUpstreamCapabilitiesChanged(capabilities);
- }
-
- private void reportConfigurationChanged(TetheringConfigurationParcel config) {
- final int length = mTetheringEventCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mTetheringEventCallbacks.getBroadcastItem(i).onConfigurationChanged(config);
- // TODO(b/148139325): send tetheringSupported on configuration change
- } catch (RemoteException e) {
- // Not really very much to do here.
- }
- }
- } finally {
- mTetheringEventCallbacks.finishBroadcast();
- }
- }
-
- private void reportTetherStateChanged(TetherStatesParcel states) {
- final int length = mTetheringEventCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mTetheringEventCallbacks.getBroadcastItem(i).onTetherStatesChanged(states);
- } catch (RemoteException e) {
- // Not really very much to do here.
- }
- }
- } finally {
- mTetheringEventCallbacks.finishBroadcast();
- }
- }
-
- private void reportTetherClientsChanged(List<TetheredClient> clients) {
- final int length = mTetheringEventCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- final CallbackCookie cookie =
- (CallbackCookie) mTetheringEventCallbacks.getBroadcastCookie(i);
- if (!cookie.hasListClientsPermission) continue;
- mTetheringEventCallbacks.getBroadcastItem(i).onTetherClientsChanged(clients);
- } catch (RemoteException e) {
- // Not really very much to do here.
- }
- }
- } finally {
- mTetheringEventCallbacks.finishBroadcast();
- }
- }
-
- private void reportOffloadStatusChanged(final int status) {
- final int length = mTetheringEventCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mTetheringEventCallbacks.getBroadcastItem(i).onOffloadStatusChanged(status);
- } catch (RemoteException e) {
- // Not really very much to do here.
- }
- }
- } finally {
- mTetheringEventCallbacks.finishBroadcast();
- }
- }
-
- // if ro.tether.denied = true we default to no tethering
- // gservices could set the secure setting to 1 though to enable it on a build where it
- // had previously been turned off.
- boolean isTetheringSupported() {
- final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1;
- final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
- final boolean tetherEnabledInSettings = tetherSupported
- && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
-
- return tetherEnabledInSettings && hasTetherableConfiguration()
- && !isProvisioningNeededButUnavailable();
- }
-
- void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
- // Binder.java closes the resource for us.
- @SuppressWarnings("resource")
- final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump.");
- return;
- }
-
- pw.println("Tethering:");
- pw.increaseIndent();
-
- pw.println("Configuration:");
- pw.increaseIndent();
- final TetheringConfiguration cfg = mConfig;
- cfg.dump(pw);
- pw.decreaseIndent();
-
- pw.println("Entitlement:");
- pw.increaseIndent();
- mEntitlementMgr.dump(pw);
- pw.decreaseIndent();
-
- synchronized (mPublicSync) {
- pw.println("Tether state:");
- pw.increaseIndent();
- for (int i = 0; i < mTetherStates.size(); i++) {
- final String iface = mTetherStates.keyAt(i);
- final TetherState tetherState = mTetherStates.valueAt(i);
- pw.print(iface + " - ");
-
- switch (tetherState.lastState) {
- case IpServer.STATE_UNAVAILABLE:
- pw.print("UnavailableState");
- break;
- case IpServer.STATE_AVAILABLE:
- pw.print("AvailableState");
- break;
- case IpServer.STATE_TETHERED:
- pw.print("TetheredState");
- break;
- case IpServer.STATE_LOCAL_ONLY:
- pw.print("LocalHotspotState");
- break;
- default:
- pw.print("UnknownState");
- break;
- }
- pw.println(" - lastError = " + tetherState.lastError);
- }
- pw.println("Upstream wanted: " + upstreamWanted());
- pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
- pw.decreaseIndent();
- }
-
- pw.println("Hardware offload:");
- pw.increaseIndent();
- mOffloadController.dump(pw);
- pw.decreaseIndent();
-
- pw.println("BPF offload:");
- pw.increaseIndent();
- mBpfCoordinator.dump(pw);
- pw.decreaseIndent();
-
- pw.println("Private address coordinator:");
- pw.increaseIndent();
- mPrivateAddressCoordinator.dump(pw);
- pw.decreaseIndent();
-
- pw.println("Log:");
- pw.increaseIndent();
- if (argsContain(args, "--short")) {
- pw.println("<log removed for brevity>");
- } else {
- mLog.dump(fd, pw, args);
- }
- pw.decreaseIndent();
-
- pw.decreaseIndent();
- }
-
- private static boolean argsContain(String[] args, String target) {
- for (String arg : args) {
- if (target.equals(arg)) return true;
- }
- return false;
- }
-
- private void updateConnectedClients(final List<WifiClient> wifiClients) {
- if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) {
- reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
- }
- }
-
- private IpServer.Callback makeControlCallback() {
- return new IpServer.Callback() {
- @Override
- public void updateInterfaceState(IpServer who, int state, int lastError) {
- notifyInterfaceStateChange(who, state, lastError);
- }
-
- @Override
- public void updateLinkProperties(IpServer who, LinkProperties newLp) {
- notifyLinkPropertiesChanged(who, newLp);
- }
-
- @Override
- public void dhcpLeasesChanged() {
- updateConnectedClients(null /* wifiClients */);
- }
-
- @Override
- public void requestEnableTethering(int tetheringType, boolean enabled) {
- enableTetheringInternal(tetheringType, enabled, null);
- }
- };
- }
-
- // TODO: Move into TetherMainSM.
- private void notifyInterfaceStateChange(IpServer who, int state, int error) {
- final String iface = who.interfaceName();
- synchronized (mPublicSync) {
- final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.ipServer.equals(who)) {
- tetherState.lastState = state;
- tetherState.lastError = error;
- } else {
- if (DBG) Log.d(TAG, "got notification from stale iface " + iface);
- }
- }
-
- mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error));
-
- // If TetherMainSM is in ErrorState, TetherMainSM stays there.
- // Thus we give a chance for TetherMainSM to recover to InitialState
- // by sending CMD_CLEAR_ERROR
- if (error == TETHER_ERROR_INTERNAL_ERROR) {
- mTetherMainSM.sendMessage(TetherMainSM.CMD_CLEAR_ERROR, who);
- }
- int which;
- switch (state) {
- case IpServer.STATE_UNAVAILABLE:
- case IpServer.STATE_AVAILABLE:
- which = TetherMainSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
- break;
- case IpServer.STATE_TETHERED:
- case IpServer.STATE_LOCAL_ONLY:
- which = TetherMainSM.EVENT_IFACE_SERVING_STATE_ACTIVE;
- break;
- default:
- Log.wtf(TAG, "Unknown interface state: " + state);
- return;
- }
- mTetherMainSM.sendMessage(which, state, 0, who);
- sendTetherStateChangedBroadcast();
- }
-
- private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) {
- final String iface = who.interfaceName();
- final int state;
- synchronized (mPublicSync) {
- final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.ipServer.equals(who)) {
- state = tetherState.lastState;
- } else {
- mLog.log("got notification from stale iface " + iface);
- return;
- }
- }
-
- mLog.log(String.format(
- "OBSERVED LinkProperties update iface=%s state=%s lp=%s",
- iface, IpServer.getStateString(state), newLp));
- final int which = TetherMainSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
- mTetherMainSM.sendMessage(which, state, 0, newLp);
- }
-
- private void maybeTrackNewInterfaceLocked(final String iface) {
- // If we don't care about this type of interface, ignore.
- final int interfaceType = ifaceNameToType(iface);
- if (interfaceType == TETHERING_INVALID) {
- mLog.log(iface + " is not a tetherable iface, ignoring");
- return;
- }
- maybeTrackNewInterfaceLocked(iface, interfaceType);
- }
-
- private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) {
- // If we have already started a TISM for this interface, skip.
- if (mTetherStates.containsKey(iface)) {
- mLog.log("active iface (" + iface + ") reported as added, ignoring");
- return;
- }
-
- mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
- final TetherState tetherState = new TetherState(
- new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
- makeControlCallback(), mConfig.enableLegacyDhcpServer,
- mConfig.isBpfOffloadEnabled(), mPrivateAddressCoordinator,
- mDeps.getIpServerDependencies()));
- mTetherStates.put(iface, tetherState);
- tetherState.ipServer.start();
- }
-
- private void stopTrackingInterfaceLocked(final String iface) {
- final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState == null) {
- mLog.log("attempting to remove unknown iface (" + iface + "), ignoring");
- return;
- }
- tetherState.ipServer.stop();
- mLog.log("removing TetheringInterfaceStateMachine for: " + iface);
- mTetherStates.remove(iface);
- }
-
- private static String[] copy(String[] strarray) {
- return Arrays.copyOf(strarray, strarray.length);
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
deleted file mode 100644
index 799637c9b1c5..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ /dev/null
@@ -1,521 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.content.Context.TELEPHONY_SERVICE;
-import static android.net.ConnectivityManager.TYPE_ETHERNET;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.TetheringConfigurationParcel;
-import android.net.util.SharedLog;
-import android.provider.DeviceConfig;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.StringJoiner;
-
-/**
- * A utility class to encapsulate the various tethering configuration elements.
- *
- * This configuration data includes elements describing upstream properties
- * (preferred and required types of upstream connectivity as well as default
- * DNS servers to use if none are available) and downstream properties (such
- * as regular expressions use to match suitable downstream interfaces and the
- * DHCPv4 ranges to use).
- *
- * @hide
- */
-public class TetheringConfiguration {
- private static final String TAG = TetheringConfiguration.class.getSimpleName();
-
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- // Default ranges used for the legacy DHCP server.
- // USB is 192.168.42.1 and 255.255.255.0
- // Wifi is 192.168.43.1 and 255.255.255.0
- // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
- // with 255.255.255.0
- // P2P is 192.168.49.1 and 255.255.255.0
- private static final String[] LEGACY_DHCP_DEFAULT_RANGE = {
- "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
- "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
- "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
- "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
- };
-
- private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
-
- /**
- * Override enabling BPF offload configuration for tethering.
- */
- public static final String OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD =
- "override_tether_enable_bpf_offload";
-
- /**
- * Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
- */
- public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
- "tether_enable_legacy_dhcp_server";
-
- public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP =
- "use_legacy_wifi_p2p_dedicated_ip";
-
- /**
- * Flag use to enable select all prefix ranges feature.
- * TODO: Remove this flag if there are no problems after M-2020-12 rolls out.
- */
- public static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
- "tether_enable_select_all_prefix_ranges";
-
- /**
- * Default value that used to periodic polls tether offload stats from tethering offload HAL
- * to make the data warnings work.
- */
- public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000;
-
- public final String[] tetherableUsbRegexs;
- public final String[] tetherableWifiRegexs;
- public final String[] tetherableWigigRegexs;
- public final String[] tetherableWifiP2pRegexs;
- public final String[] tetherableBluetoothRegexs;
- public final String[] tetherableNcmRegexs;
- public final boolean isDunRequired;
- public final boolean chooseUpstreamAutomatically;
- public final Collection<Integer> preferredUpstreamIfaceTypes;
- public final String[] legacyDhcpRanges;
- public final String[] defaultIPv4DNS;
- public final boolean enableLegacyDhcpServer;
-
- public final String[] provisioningApp;
- public final String provisioningAppNoUi;
- public final int provisioningCheckPeriod;
- public final String provisioningResponse;
-
- public final int activeDataSubId;
-
- private final int mOffloadPollInterval;
- // TODO: Add to TetheringConfigurationParcel if required.
- private final boolean mEnableBpfOffload;
- private final boolean mEnableWifiP2pDedicatedIp;
-
- private final boolean mEnableSelectAllPrefixRange;
-
- public TetheringConfiguration(Context ctx, SharedLog log, int id) {
- final SharedLog configLog = log.forSubComponent("config");
-
- activeDataSubId = id;
- Resources res = getResources(ctx, activeDataSubId);
-
- tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
- tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs);
- // TODO: Evaluate deleting this altogether now that Wi-Fi always passes
- // us an interface name. Careful consideration needs to be given to
- // implications for Settings and for provisioning checks.
- tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs);
- tetherableWigigRegexs = getResourceStringArray(res, R.array.config_tether_wigig_regexs);
- tetherableWifiP2pRegexs = getResourceStringArray(
- res, R.array.config_tether_wifi_p2p_regexs);
- tetherableBluetoothRegexs = getResourceStringArray(
- res, R.array.config_tether_bluetooth_regexs);
-
- isDunRequired = checkDunRequired(ctx);
-
- chooseUpstreamAutomatically = getResourceBoolean(
- res, R.bool.config_tether_upstream_automatic, false /** defaultValue */);
- preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
-
- legacyDhcpRanges = getLegacyDhcpRanges(res);
- defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
- mEnableBpfOffload = getEnableBpfOffload(res);
- enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
-
- provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
- provisioningAppNoUi = getResourceString(res,
- R.string.config_mobile_hotspot_provision_app_no_ui);
- provisioningCheckPeriod = getResourceInteger(res,
- R.integer.config_mobile_hotspot_provision_check_period,
- 0 /* No periodic re-check */);
- provisioningResponse = getResourceString(res,
- R.string.config_mobile_hotspot_provision_response);
-
- mOffloadPollInterval = getResourceInteger(res,
- R.integer.config_tether_offload_poll_interval,
- DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
-
- mEnableWifiP2pDedicatedIp = getResourceBoolean(res,
- R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
- false /* defaultValue */);
-
- // Flags should normally not be booleans, but this is a kill-switch flag that is only used
- // to turn off the feature, so binary rollback problems do not apply.
- mEnableSelectAllPrefixRange = getDeviceConfigBoolean(
- TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true /* defaultValue */);
-
- configLog.log(toString());
- }
-
- /** Check whether input interface belong to usb.*/
- public boolean isUsb(String iface) {
- return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
- }
-
- /** Check whether input interface belong to wifi.*/
- public boolean isWifi(String iface) {
- return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
- }
-
- /** Check whether input interface belong to wigig.*/
- public boolean isWigig(String iface) {
- return matchesDownstreamRegexs(iface, tetherableWigigRegexs);
- }
-
- /** Check whether this interface is Wifi P2P interface. */
- public boolean isWifiP2p(String iface) {
- return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs);
- }
-
- /** Check whether using legacy mode for wifi P2P. */
- public boolean isWifiP2pLegacyTetheringMode() {
- return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0);
- }
-
- /** Check whether input interface belong to bluetooth.*/
- public boolean isBluetooth(String iface) {
- return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
- }
-
- /** Check if interface is ncm */
- public boolean isNcm(String iface) {
- return matchesDownstreamRegexs(iface, tetherableNcmRegexs);
- }
-
- /** Check whether no ui entitlement application is available.*/
- public boolean hasMobileHotspotProvisionApp() {
- return !TextUtils.isEmpty(provisioningAppNoUi);
- }
-
- /** Check whether dedicated wifi p2p address is enabled. */
- public boolean shouldEnableWifiP2pDedicatedIp() {
- return mEnableWifiP2pDedicatedIp;
- }
-
- /** Does the dumping.*/
- public void dump(PrintWriter pw) {
- pw.print("activeDataSubId: ");
- pw.println(activeDataSubId);
-
- dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
- dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
- dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs);
- dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
- dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs);
-
- pw.print("isDunRequired: ");
- pw.println(isDunRequired);
-
- pw.print("chooseUpstreamAutomatically: ");
- pw.println(chooseUpstreamAutomatically);
- pw.print("legacyPreredUpstreamIfaceTypes: ");
- pw.println(Arrays.toString(toIntArray(preferredUpstreamIfaceTypes)));
-
- dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges);
- dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
-
- pw.print("offloadPollInterval: ");
- pw.println(mOffloadPollInterval);
-
- dumpStringArray(pw, "provisioningApp", provisioningApp);
- pw.print("provisioningAppNoUi: ");
- pw.println(provisioningAppNoUi);
-
- pw.print("enableBpfOffload: ");
- pw.println(mEnableBpfOffload);
-
- pw.print("enableLegacyDhcpServer: ");
- pw.println(enableLegacyDhcpServer);
-
- pw.print("enableWifiP2pDedicatedIp: ");
- pw.println(mEnableWifiP2pDedicatedIp);
-
- pw.print("mEnableSelectAllPrefixRange: ");
- pw.println(mEnableSelectAllPrefixRange);
- }
-
- /** Returns the string representation of this object.*/
- public String toString() {
- final StringJoiner sj = new StringJoiner(" ");
- sj.add(String.format("activeDataSubId:%d", activeDataSubId));
- sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
- sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
- sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs)));
- sj.add(String.format("tetherableBluetoothRegexs:%s",
- makeString(tetherableBluetoothRegexs)));
- sj.add(String.format("isDunRequired:%s", isDunRequired));
- sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
- sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval));
- sj.add(String.format("preferredUpstreamIfaceTypes:%s",
- toIntArray(preferredUpstreamIfaceTypes)));
- sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
- sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
- sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload));
- sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
- return String.format("TetheringConfiguration{%s}", sj.toString());
- }
-
- private static void dumpStringArray(PrintWriter pw, String label, String[] values) {
- pw.print(label);
- pw.print(": ");
-
- if (values != null) {
- final StringJoiner sj = new StringJoiner(", ", "[", "]");
- for (String value : values) sj.add(value);
- pw.print(sj.toString());
- } else {
- pw.print("null");
- }
-
- pw.println();
- }
-
- private static String makeString(String[] strings) {
- if (strings == null) return "null";
- final StringJoiner sj = new StringJoiner(",", "[", "]");
- for (String s : strings) sj.add(s);
- return sj.toString();
- }
-
- /** Check whether dun is required. */
- public static boolean checkDunRequired(Context ctx) {
- final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
- // TelephonyManager would uses the active data subscription, which should be the one used
- // by tethering.
- return (tm != null) ? tm.isTetheringApnRequired() : false;
- }
-
- public int getOffloadPollInterval() {
- return mOffloadPollInterval;
- }
-
- public boolean isBpfOffloadEnabled() {
- return mEnableBpfOffload;
- }
-
- public boolean isSelectAllPrefixRangeEnabled() {
- return mEnableSelectAllPrefixRange;
- }
-
- private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
- final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
- final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
- for (int i : ifaceTypes) {
- switch (i) {
- case TYPE_MOBILE:
- case TYPE_MOBILE_HIPRI:
- if (dunRequired) continue;
- break;
- case TYPE_MOBILE_DUN:
- if (!dunRequired) continue;
- break;
- }
- upstreamIfaceTypes.add(i);
- }
-
- // Fix up upstream interface types for DUN or mobile. NOTE: independent
- // of the value of |dunRequired|, cell data of one form or another is
- // *always* an upstream, regardless of the upstream interface types
- // specified by configuration resources.
- if (dunRequired) {
- appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN);
- } else {
- // Do not modify if a cellular interface type is already present in the
- // upstream interface types. Add TYPE_MOBILE and TYPE_MOBILE_HIPRI if no
- // cellular interface types are found in the upstream interface types.
- // This preserves backwards compatibility and prevents the DUN and default
- // mobile types incorrectly appearing together, which could happen on
- // previous releases in the common case where checkDunRequired returned
- // DUN_UNSPECIFIED.
- if (!containsOneOf(upstreamIfaceTypes, TYPE_MOBILE, TYPE_MOBILE_HIPRI)) {
- upstreamIfaceTypes.add(TYPE_MOBILE);
- upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
- }
- }
-
- // Always make sure our good friend Ethernet is present.
- // TODO: consider unilaterally forcing this at the front.
- prependIfNotPresent(upstreamIfaceTypes, TYPE_ETHERNET);
-
- return upstreamIfaceTypes;
- }
-
- private static boolean matchesDownstreamRegexs(String iface, String[] regexs) {
- for (String regex : regexs) {
- if (iface.matches(regex)) return true;
- }
- return false;
- }
-
- private static String[] getLegacyDhcpRanges(Resources res) {
- final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range);
- if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
- return fromResource;
- }
- return copy(LEGACY_DHCP_DEFAULT_RANGE);
- }
-
- private static String getResourceString(Resources res, final int resId) {
- try {
- return res.getString(resId);
- } catch (Resources.NotFoundException e) {
- return "";
- }
- }
-
- private static boolean getResourceBoolean(Resources res, int resId, boolean defaultValue) {
- try {
- return res.getBoolean(resId);
- } catch (Resources.NotFoundException e404) {
- return defaultValue;
- }
- }
-
- private static String[] getResourceStringArray(Resources res, int resId) {
- try {
- final String[] strArray = res.getStringArray(resId);
- return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
- } catch (Resources.NotFoundException e404) {
- return EMPTY_STRING_ARRAY;
- }
- }
-
- private static int getResourceInteger(Resources res, int resId, int defaultValue) {
- try {
- return res.getInteger(resId);
- } catch (Resources.NotFoundException e404) {
- return defaultValue;
- }
- }
-
- private boolean getEnableBpfOffload(final Resources res) {
- // Get BPF offload config
- // Priority 1: Device config
- // Priority 2: Resource config
- // Priority 3: Default value
- final boolean defaultValue = getResourceBoolean(
- res, R.bool.config_tether_enable_bpf_offload, true /** default value */);
-
- return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue);
- }
-
- private boolean getEnableLegacyDhcpServer(final Resources res) {
- return getResourceBoolean(
- res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */)
- || getDeviceConfigBoolean(
- TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */);
- }
-
- private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
- // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead
- // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the
- // returned boolean value comes from device config or default value (because of null
- // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java.
- final String value = getDeviceConfigProperty(name);
- return value != null ? Boolean.parseBoolean(value) : defaultValue;
- }
-
- @VisibleForTesting
- protected String getDeviceConfigProperty(String name) {
- return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name);
- }
-
- private Resources getResources(Context ctx, int subId) {
- if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return getResourcesForSubIdWrapper(ctx, subId);
- } else {
- return ctx.getResources();
- }
- }
-
- @VisibleForTesting
- protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
- return SubscriptionManager.getResourcesForSubId(ctx, subId);
- }
-
- private static String[] copy(String[] strarray) {
- return Arrays.copyOf(strarray, strarray.length);
- }
-
- private static void prependIfNotPresent(ArrayList<Integer> list, int value) {
- if (list.contains(value)) return;
- list.add(0, value);
- }
-
- private static void appendIfNotPresent(ArrayList<Integer> list, int value) {
- if (list.contains(value)) return;
- list.add(value);
- }
-
- private static boolean containsOneOf(ArrayList<Integer> list, Integer... values) {
- for (Integer value : values) {
- if (list.contains(value)) return true;
- }
- return false;
- }
-
- private static int[] toIntArray(Collection<Integer> values) {
- final int[] result = new int[values.size()];
- int index = 0;
- for (Integer value : values) {
- result[index++] = value;
- }
- return result;
- }
-
- /**
- * Convert this TetheringConfiguration to a TetheringConfigurationParcel.
- */
- public TetheringConfigurationParcel toStableParcelable() {
- final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel();
- parcel.subId = activeDataSubId;
- parcel.tetherableUsbRegexs = tetherableUsbRegexs;
- parcel.tetherableWifiRegexs = tetherableWifiRegexs;
- parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs;
- parcel.isDunRequired = isDunRequired;
- parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically;
-
- parcel.preferredUpstreamIfaceTypes = toIntArray(preferredUpstreamIfaceTypes);
-
- parcel.legacyDhcpRanges = legacyDhcpRanges;
- parcel.defaultIPv4DNS = defaultIPv4DNS;
- parcel.enableLegacyDhcpServer = enableLegacyDhcpServer;
- parcel.provisioningApp = provisioningApp;
- parcel.provisioningAppNoUi = provisioningAppNoUi;
- parcel.provisioningCheckPeriod = provisioningCheckPeriod;
- return parcel;
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
deleted file mode 100644
index 45b914178e97..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import android.app.usage.NetworkStatsManager;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.net.INetd;
-import android.net.NetworkRequest;
-import android.net.ip.IpServer;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.util.StateMachine;
-
-import java.util.ArrayList;
-
-
-/**
- * Capture tethering dependencies, for injection.
- *
- * @hide
- */
-public abstract class TetheringDependencies {
- /**
- * Get a reference to the BpfCoordinator to be used by tethering.
- */
- public @NonNull BpfCoordinator getBpfCoordinator(
- @NonNull BpfCoordinator.Dependencies deps) {
- return new BpfCoordinator(deps);
- }
-
- /**
- * Get a reference to the offload hardware interface to be used by tethering.
- */
- public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
- return new OffloadHardwareInterface(h, log);
- }
-
- /**
- * Get a reference to the offload controller to be used by tethering.
- */
- @NonNull
- public OffloadController getOffloadController(@NonNull Handler h,
- @NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) {
- final NetworkStatsManager statsManager =
- (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
- return new OffloadController(h, getOffloadHardwareInterface(h, log),
- getContext().getContentResolver(), statsManager, log, deps);
- }
-
-
- /**
- * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
- */
- public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
- SharedLog log, int what) {
- return new UpstreamNetworkMonitor(ctx, target, log, what);
- }
-
- /**
- * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
- */
- public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
- ArrayList<IpServer> notifyList, SharedLog log) {
- return new IPv6TetheringCoordinator(notifyList, log);
- }
-
- /**
- * Get dependencies to be used by IpServer.
- */
- public abstract IpServer.Dependencies getIpServerDependencies();
-
- /**
- * Indicates whether tethering is supported on the device.
- */
- public boolean isTetheringSupported() {
- return true;
- }
-
- /**
- * Get the NetworkRequest that should be fulfilled by the default network.
- */
- public abstract NetworkRequest getDefaultNetworkRequest();
-
- /**
- * Get a reference to the EntitlementManager to be used by tethering.
- */
- public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
- Runnable callback) {
- return new EntitlementManager(ctx, h, log, callback);
- }
-
- /**
- * Generate a new TetheringConfiguration according to input sub Id.
- */
- public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
- int subId) {
- return new TetheringConfiguration(ctx, log, subId);
- }
-
- /**
- * Get a reference to INetd to be used by tethering.
- */
- public INetd getINetd(Context context) {
- return INetd.Stub.asInterface(
- (IBinder) context.getSystemService(Context.NETD_SERVICE));
- }
-
- /**
- * Get a reference to the TetheringNotificationUpdater to be used by tethering.
- */
- public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx,
- @NonNull final Looper looper) {
- return new TetheringNotificationUpdater(ctx, looper);
- }
-
- /**
- * Get tethering thread looper.
- */
- public abstract Looper getTetheringLooper();
-
- /**
- * Get Context of TetheringSerice.
- */
- public abstract Context getContext();
-
- /**
- * Get a reference to BluetoothAdapter to be used by tethering.
- */
- public abstract BluetoothAdapter getBluetoothAdapter();
-
- /**
- * Get SystemProperties which indicate whether tethering is denied.
- */
- public boolean isTetheringDenied() {
- return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true");
- }
-
- /**
- * Get a reference to PrivateAddressCoordinator to be used by Tethering.
- */
- public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
- TetheringConfiguration cfg) {
- return new PrivateAddressCoordinator(ctx, cfg);
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
deleted file mode 100644
index ff38f717a121..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import android.annotation.Nullable;
-import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
-import android.net.RouteInfo;
-import android.net.util.InterfaceSet;
-
-import com.android.net.module.util.NetUtils;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * @hide
- */
-public final class TetheringInterfaceUtils {
- private static final InetAddress IN6ADDR_ANY = getByAddress(
- new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
- private static final InetAddress INADDR_ANY = getByAddress(new byte[] {0, 0, 0, 0});
-
- /**
- * Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
- * @return null if there is no usable interface, or a set of at least one interface otherwise.
- */
- public static @Nullable InterfaceSet getTetheringInterfaces(UpstreamNetworkState ns) {
- if (ns == null) {
- return null;
- }
-
- final LinkProperties lp = ns.linkProperties;
- final String if4 = getInterfaceForDestination(lp, INADDR_ANY);
- final String if6 = getIPv6Interface(ns);
-
- return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
- }
-
- /**
- * Get the upstream interface for IPv6 tethering.
- * @return null if there is no usable interface, or the interface name otherwise.
- */
- public static @Nullable String getIPv6Interface(UpstreamNetworkState ns) {
- // Broadly speaking:
- //
- // [1] does the upstream have an IPv6 default route?
- //
- // and
- //
- // [2] does the upstream have one or more global IPv6 /64s
- // dedicated to this device?
- //
- // In lieu of Prefix Delegation and other evaluation of whether a
- // prefix may or may not be dedicated to this device, for now just
- // check whether the upstream is TRANSPORT_CELLULAR. This works
- // because "[t]he 3GPP network allocates each default bearer a unique
- // /64 prefix", per RFC 6459, Section 5.2.
- final boolean canTether =
- (ns != null) && (ns.network != null)
- && (ns.linkProperties != null) && (ns.networkCapabilities != null)
- // At least one upstream DNS server:
- && ns.linkProperties.hasIpv6DnsServer()
- // Minimal amount of IPv6 provisioning:
- && ns.linkProperties.hasGlobalIpv6Address()
- // Temporary approximation of "dedicated prefix":
- && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
-
- return canTether
- ? getInterfaceForDestination(ns.linkProperties, IN6ADDR_ANY)
- : null;
- }
-
- private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
- final RouteInfo ri = (lp != null)
- ? NetUtils.selectBestRoute(lp.getAllRoutes(), dst)
- : null;
- return (ri != null) ? ri.getInterface() : null;
- }
-
- private static InetAddress getByAddress(final byte[] addr) {
- try {
- return InetAddress.getByAddress(null, addr);
- } catch (UnknownHostException e) {
- throw new AssertionError("illegal address length" + addr.length);
- }
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
deleted file mode 100644
index a0198cc9c126..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ /dev/null
@@ -1,362 +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.networkstack.tethering;
-
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
-import static android.text.TextUtils.isEmpty;
-
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.net.NetworkCapabilities;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.SparseArray;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.IntDef;
-import androidx.annotation.IntRange;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A class to display tethering-related notifications.
- *
- * <p>This class is not thread safe, it is intended to be used only from the tethering handler
- * thread. However the constructor is an exception, as it is called on another thread ;
- * therefore for thread safety all members of this class MUST either be final or initialized
- * to their default value (0, false or null).
- *
- * @hide
- */
-public class TetheringNotificationUpdater {
- private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
- private static final String CHANNEL_ID = "TETHERING_STATUS";
- private static final String WIFI_DOWNSTREAM = "WIFI";
- private static final String USB_DOWNSTREAM = "USB";
- private static final String BLUETOOTH_DOWNSTREAM = "BT";
- @VisibleForTesting
- static final String ACTION_DISABLE_TETHERING =
- "com.android.server.connectivity.tethering.DISABLE_TETHERING";
- private static final boolean NOTIFY_DONE = true;
- private static final boolean NO_NOTIFY = false;
- @VisibleForTesting
- static final int EVENT_SHOW_NO_UPSTREAM = 1;
- // Id to update and cancel restricted notification. Must be unique within the tethering app.
- @VisibleForTesting
- static final int RESTRICTED_NOTIFICATION_ID = 1001;
- // Id to update and cancel no upstream notification. Must be unique within the tethering app.
- @VisibleForTesting
- static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
- // Id to update and cancel roaming notification. Must be unique within the tethering app.
- @VisibleForTesting
- static final int ROAMING_NOTIFICATION_ID = 1003;
- @VisibleForTesting
- static final int NO_ICON_ID = 0;
- @VisibleForTesting
- static final int DOWNSTREAM_NONE = 0;
- // Refer to TelephonyManager#getSimCarrierId for more details about carrier id.
- @VisibleForTesting
- static final int VERIZON_CARRIER_ID = 1839;
- private final Context mContext;
- private final NotificationManager mNotificationManager;
- private final NotificationChannel mChannel;
- private final Handler mHandler;
-
- // WARNING : the constructor is called on a different thread. Thread safety therefore
- // relies on these values being initialized to 0, false or null, and not any other value. If you
- // need to change this, you will need to change the thread where the constructor is invoked, or
- // to introduce synchronization.
- // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
- // This value has to be made 1 2 and 4, and OR'd with the others.
- private int mDownstreamTypesMask = DOWNSTREAM_NONE;
- private boolean mNoUpstream = false;
- private boolean mRoaming = false;
-
- // WARNING : this value is not able to being initialized to 0 and must have volatile because
- // telephony service is not guaranteed that is up before tethering service starts. If telephony
- // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid
- // subscription id(0) to query resources. Therefore, initialized subscription id must be
- // INVALID_SUBSCRIPTION_ID.
- private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- RESTRICTED_NOTIFICATION_ID,
- NO_UPSTREAM_NOTIFICATION_ID,
- ROAMING_NOTIFICATION_ID
- })
- @interface NotificationId {}
-
- private static final class MccMncOverrideInfo {
- public final String visitedMccMnc;
- public final int homeMcc;
- public final int homeMnc;
- MccMncOverrideInfo(String visitedMccMnc, int mcc, int mnc) {
- this.visitedMccMnc = visitedMccMnc;
- this.homeMcc = mcc;
- this.homeMnc = mnc;
- }
- }
-
- private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>();
-
- static {
- sCarrierIdToMccMnc.put(VERIZON_CARRIER_ID, new MccMncOverrideInfo("20404", 311, 480));
- }
-
- public TetheringNotificationUpdater(@NonNull final Context context,
- @NonNull final Looper looper) {
- mContext = context;
- mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
- .getSystemService(Context.NOTIFICATION_SERVICE);
- mChannel = new NotificationChannel(
- CHANNEL_ID,
- context.getResources().getString(R.string.notification_channel_tethering_status),
- NotificationManager.IMPORTANCE_LOW);
- mNotificationManager.createNotificationChannel(mChannel);
- mHandler = new NotificationHandler(looper);
- }
-
- private class NotificationHandler extends Handler {
- NotificationHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case EVENT_SHOW_NO_UPSTREAM:
- notifyTetheringNoUpstream();
- break;
- }
- }
- }
-
- /** Called when downstream has changed */
- public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
- updateActiveNotifications(
- mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming);
- }
-
- /** Called when active data subscription id changed */
- public void onActiveDataSubscriptionIdChanged(final int subId) {
- updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming);
- }
-
- /** Called when upstream network capabilities changed */
- public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) {
- final boolean isNoUpstream = (capabilities == null);
- final boolean isRoaming = capabilities != null
- && !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- updateActiveNotifications(
- mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming);
- }
-
- @NonNull
- @VisibleForTesting
- final Handler getHandler() {
- return mHandler;
- }
-
- @NonNull
- @VisibleForTesting
- Resources getResourcesForSubId(@NonNull final Context context, final int subId) {
- final Resources res = SubscriptionManager.getResourcesForSubId(context, subId);
- final TelephonyManager tm =
- ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
- .createForSubscriptionId(mActiveDataSubId);
- final int carrierId = tm.getSimCarrierId();
- final String mccmnc = tm.getSimOperator();
- final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId);
- if (overrideInfo != null && overrideInfo.visitedMccMnc.equals(mccmnc)) {
- // Re-configure MCC/MNC value to specific carrier to get right resources.
- final Configuration config = res.getConfiguration();
- config.mcc = overrideInfo.homeMcc;
- config.mnc = overrideInfo.homeMnc;
- return context.createConfigurationContext(config).getResources();
- }
- return res;
- }
-
- private void updateActiveNotifications(final int subId, final int downstreamTypes,
- final boolean noUpstream, final boolean isRoaming) {
- final boolean tetheringActiveChanged =
- (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE);
- final boolean subIdChanged = subId != mActiveDataSubId;
- final boolean upstreamChanged = noUpstream != mNoUpstream;
- final boolean roamingChanged = isRoaming != mRoaming;
- final boolean updateAll = tetheringActiveChanged || subIdChanged;
- mActiveDataSubId = subId;
- mDownstreamTypesMask = downstreamTypes;
- mNoUpstream = noUpstream;
- mRoaming = isRoaming;
-
- if (updateAll || upstreamChanged) updateNoUpstreamNotification();
- if (updateAll || roamingChanged) updateRoamingNotification();
- }
-
- private void updateNoUpstreamNotification() {
- final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
-
- if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) {
- clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
- mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
- }
- }
-
- private void updateRoamingNotification() {
- final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
-
- if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) {
- clearNotification(ROAMING_NOTIFICATION_ID);
- }
- }
-
- @VisibleForTesting
- void tetheringRestrictionLifted() {
- clearNotification(RESTRICTED_NOTIFICATION_ID);
- }
-
- private void clearNotification(@NotificationId final int id) {
- mNotificationManager.cancel(null /* tag */, id);
- }
-
- @VisibleForTesting
- static String getSettingsPackageName(@NonNull final PackageManager pm) {
- final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
- final ComponentName settingsComponent = settingsIntent.resolveActivity(pm);
- return settingsComponent != null
- ? settingsComponent.getPackageName() : "com.android.settings";
- }
-
- @VisibleForTesting
- void notifyTetheringDisabledByRestriction() {
- final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
- final String title = res.getString(R.string.disable_tether_notification_title);
- final String message = res.getString(R.string.disable_tether_notification_message);
- if (isEmpty(title) || isEmpty(message)) return;
-
- final PendingIntent pi = PendingIntent.getActivity(
- mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
- 0 /* requestCode */,
- new Intent(Settings.ACTION_TETHER_SETTINGS)
- .setPackage(getSettingsPackageName(mContext.getPackageManager()))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
- PendingIntent.FLAG_IMMUTABLE,
- null /* options */);
-
- showNotification(R.drawable.stat_sys_tether_general, title, message,
- RESTRICTED_NOTIFICATION_ID, false /* ongoing */, pi, new Action[0]);
- }
-
- private void notifyTetheringNoUpstream() {
- final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
- final String title = res.getString(R.string.no_upstream_notification_title);
- final String message = res.getString(R.string.no_upstream_notification_message);
- final String disableButton =
- res.getString(R.string.no_upstream_notification_disable_button);
- if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return;
-
- final Intent intent = new Intent(ACTION_DISABLE_TETHERING);
- intent.setPackage(mContext.getPackageName());
- final PendingIntent pi = PendingIntent.getBroadcast(
- mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
- 0 /* requestCode */,
- intent,
- PendingIntent.FLAG_IMMUTABLE);
- final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build();
-
- showNotification(R.drawable.stat_sys_tether_general, title, message,
- NO_UPSTREAM_NOTIFICATION_ID, true /* ongoing */, null /* pendingIntent */, action);
- }
-
- private boolean setupRoamingNotification() {
- final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
- final boolean upstreamRoamingNotification =
- res.getBoolean(R.bool.config_upstream_roaming_notification);
-
- if (!upstreamRoamingNotification) return NO_NOTIFY;
-
- final String title = res.getString(R.string.upstream_roaming_notification_title);
- final String message = res.getString(R.string.upstream_roaming_notification_message);
- if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;
-
- final PendingIntent pi = PendingIntent.getActivity(
- mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
- 0 /* requestCode */,
- new Intent(Settings.ACTION_TETHER_SETTINGS)
- .setPackage(getSettingsPackageName(mContext.getPackageManager()))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
- PendingIntent.FLAG_IMMUTABLE,
- null /* options */);
-
- showNotification(R.drawable.stat_sys_tether_general, title, message,
- ROAMING_NOTIFICATION_ID, true /* ongoing */, pi, new Action[0]);
- return NOTIFY_DONE;
- }
-
- private boolean setupNoUpstreamNotification() {
- final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
- final int delayToShowUpstreamNotification =
- res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul);
-
- if (delayToShowUpstreamNotification < 0) return NO_NOTIFY;
-
- mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM),
- delayToShowUpstreamNotification);
- return NOTIFY_DONE;
- }
-
- private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
- @NonNull final String message, @NotificationId final int id, final boolean ongoing,
- @Nullable PendingIntent pi, @NonNull final Action... actions) {
- final Notification notification =
- new Notification.Builder(mContext, mChannel.getId())
- .setSmallIcon(iconId)
- .setContentTitle(title)
- .setContentText(message)
- .setOngoing(ongoing)
- .setColor(mContext.getColor(
- android.R.color.system_notification_accent_color))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setCategory(Notification.CATEGORY_STATUS)
- .setContentIntent(pi)
- .setActions(actions)
- .build();
-
- mNotificationManager.notify(null /* tag */, id, notification);
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
deleted file mode 100644
index d637ba7557fa..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ /dev/null
@@ -1,389 +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.networkstack.tethering;
-
-import static android.Manifest.permission.ACCESS_NETWORK_STATE;
-import static android.Manifest.permission.NETWORK_STACK;
-import static android.Manifest.permission.TETHER_PRIVILEGED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
-import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
-import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
-
-import android.app.Service;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.content.Intent;
-import android.net.IIntResultListener;
-import android.net.INetworkStackConnector;
-import android.net.ITetheringConnector;
-import android.net.ITetheringEventCallback;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.NetworkStack;
-import android.net.TetheringRequestParcel;
-import android.net.dhcp.DhcpServerCallbacks;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.ip.IpServer;
-import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Android service used to manage tethering.
- *
- * <p>The service returns a binder for the system server to communicate with the tethering.
- */
-public class TetheringService extends Service {
- private static final String TAG = TetheringService.class.getSimpleName();
-
- private TetheringConnector mConnector;
-
- @Override
- public void onCreate() {
- final TetheringDependencies deps = makeTetheringDependencies();
- // The Tethering object needs a fully functional context to start, so this can't be done
- // in the constructor.
- mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);
- }
-
- /**
- * Make a reference to Tethering object.
- */
- @VisibleForTesting
- public Tethering makeTethering(TetheringDependencies deps) {
- System.loadLibrary("tetherutilsjni");
- return new Tethering(deps);
- }
-
- @NonNull
- @Override
- public IBinder onBind(Intent intent) {
- return mConnector;
- }
-
- private static class TetheringConnector extends ITetheringConnector.Stub {
- private final TetheringService mService;
- private final Tethering mTethering;
-
- TetheringConnector(Tethering tether, TetheringService service) {
- mTethering = tether;
- mService = service;
- }
-
- @Override
- public void tether(String iface, String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- listener.onResult(mTethering.tether(iface));
- } catch (RemoteException e) { }
- }
-
- @Override
- public void untether(String iface, String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- listener.onResult(mTethering.untether(iface));
- } catch (RemoteException e) { }
- }
-
- @Override
- public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- listener.onResult(mTethering.setUsbTethering(enable));
- } catch (RemoteException e) { }
- }
-
- @Override
- public void startTethering(TetheringRequestParcel request, String callerPkg,
- String callingAttributionTag, IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg,
- callingAttributionTag,
- request.exemptFromEntitlementCheck /* onlyAllowPrivileged */,
- listener)) {
- return;
- }
-
- mTethering.startTethering(request, listener);
- }
-
- @Override
- public void stopTethering(int type, String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- mTethering.stopTethering(type);
- listener.onResult(TETHER_ERROR_NO_ERROR);
- } catch (RemoteException e) { }
- }
-
- @Override
- public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
- boolean showEntitlementUi, String callerPkg, String callingAttributionTag) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, receiver)) return;
-
- mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
- }
-
- @Override
- public void registerTetheringEventCallback(ITetheringEventCallback callback,
- String callerPkg) {
- try {
- if (!hasTetherAccessPermission()) {
- callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
- return;
- }
- mTethering.registerTetheringEventCallback(callback);
- } catch (RemoteException e) { }
- }
-
- @Override
- public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
- String callerPkg) {
- try {
- if (!hasTetherAccessPermission()) {
- callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
- return;
- }
- mTethering.unregisterTetheringEventCallback(callback);
- } catch (RemoteException e) { }
- }
-
- @Override
- public void stopAllTethering(String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- mTethering.untetherAll();
- listener.onResult(TETHER_ERROR_NO_ERROR);
- } catch (RemoteException e) { }
- }
-
- @Override
- public void isTetheringSupported(String callerPkg, String callingAttributionTag,
- IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
- try {
- listener.onResult(TETHER_ERROR_NO_ERROR);
- } catch (RemoteException e) { }
- }
-
- @Override
- protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
- @Nullable String[] args) {
- mTethering.dump(fd, writer, args);
- }
-
- private boolean checkAndNotifyCommonError(final String callerPkg,
- final String callingAttributionTag, final IIntResultListener listener) {
- return checkAndNotifyCommonError(callerPkg, callingAttributionTag,
- false /* onlyAllowPrivileged */, listener);
- }
-
- private boolean checkAndNotifyCommonError(final String callerPkg,
- final String callingAttributionTag, final boolean onlyAllowPrivileged,
- final IIntResultListener listener) {
- try {
- if (!hasTetherChangePermission(callerPkg, callingAttributionTag,
- onlyAllowPrivileged)) {
- listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- return true;
- }
- if (!mTethering.isTetheringSupported()) {
- listener.onResult(TETHER_ERROR_UNSUPPORTED);
- return true;
- }
- } catch (RemoteException e) {
- return true;
- }
-
- return false;
- }
-
- private boolean checkAndNotifyCommonError(final String callerPkg,
- final String callingAttributionTag, final ResultReceiver receiver) {
- if (!hasTetherChangePermission(callerPkg, callingAttributionTag,
- false /* onlyAllowPrivileged */)) {
- receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null);
- return true;
- }
- if (!mTethering.isTetheringSupported()) {
- receiver.send(TETHER_ERROR_UNSUPPORTED, null);
- return true;
- }
-
- return false;
- }
-
- private boolean hasNetworkStackPermission() {
- return checkCallingOrSelfPermission(NETWORK_STACK)
- || checkCallingOrSelfPermission(PERMISSION_MAINLINE_NETWORK_STACK);
- }
-
- private boolean hasTetherPrivilegedPermission() {
- return checkCallingOrSelfPermission(TETHER_PRIVILEGED);
- }
-
- private boolean checkCallingOrSelfPermission(final String permission) {
- return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
- }
-
- private boolean hasTetherChangePermission(final String callerPkg,
- final String callingAttributionTag, final boolean onlyAllowPrivileged) {
- if (onlyAllowPrivileged && !hasNetworkStackPermission()) return false;
-
- if (hasTetherPrivilegedPermission()) return true;
-
- if (mTethering.isTetherProvisioningRequired()) return false;
-
- int uid = Binder.getCallingUid();
-
- // If callerPkg's uid is not same as Binder.getCallingUid(),
- // checkAndNoteWriteSettingsOperation will return false and the operation will be
- // denied.
- return mService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg,
- callingAttributionTag, false /* throwException */);
- }
-
- private boolean hasTetherAccessPermission() {
- if (hasTetherPrivilegedPermission()) return true;
-
- return mService.checkCallingOrSelfPermission(
- ACCESS_NETWORK_STATE) == PERMISSION_GRANTED;
- }
- }
-
- /**
- * Check if the package is a allowed to write settings. This also accounts that such an access
- * happened.
- *
- * @return {@code true} iff the package is allowed to write settings.
- */
- @VisibleForTesting
- boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
- @NonNull String callingPackage, @Nullable String callingAttributionTag,
- boolean throwException) {
- return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
- callingAttributionTag, throwException);
- }
-
- /**
- * An injection method for testing.
- */
- @VisibleForTesting
- public TetheringDependencies makeTetheringDependencies() {
- return new TetheringDependencies() {
- @Override
- public NetworkRequest getDefaultNetworkRequest() {
- // TODO: b/147280869, add a proper system API to replace this.
- final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
- return trackDefaultRequest;
- }
-
- @Override
- public Looper getTetheringLooper() {
- final HandlerThread tetherThread = new HandlerThread("android.tethering");
- tetherThread.start();
- return tetherThread.getLooper();
- }
-
- @Override
- public Context getContext() {
- return TetheringService.this;
- }
-
- @Override
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies() {
- @Override
- public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
- DhcpServerCallbacks cb) {
- try {
- final INetworkStackConnector service = getNetworkStackConnector();
- if (service == null) return;
-
- service.makeDhcpServer(ifName, params, cb);
- } catch (RemoteException e) {
- Log.e(TAG, "Fail to make dhcp server");
- try {
- cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
- } catch (RemoteException re) { }
- }
- }
- };
- }
-
- // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
- // networkStackClient.
- static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
- private INetworkStackConnector getNetworkStackConnector() {
- IBinder connector;
- try {
- final long before = System.currentTimeMillis();
- while ((connector = NetworkStack.getService()) == null) {
- if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
- Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
- return null;
- }
- Thread.sleep(200);
- }
- } catch (InterruptedException e) {
- Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
- return null;
- }
- return INetworkStackConnector.Stub.asInterface(connector);
- }
-
- @Override
- public BluetoothAdapter getBluetoothAdapter() {
- return BluetoothAdapter.getDefaultAdapter();
- }
- };
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
deleted file mode 100644
index b17065cb7804..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
-import static android.net.ConnectivityManager.TYPE_ETHERNET;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.IpPrefix;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.util.PrefixUtils;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.util.Log;
-import android.util.SparseIntArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.StateMachine;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-
-/**
- * A class to centralize all the network and link properties information
- * pertaining to the current and any potential upstream network.
- *
- * The owner of UNM gets it to register network callbacks by calling the
- * following methods :
- * Calling #startTrackDefaultNetwork() to track the system default network.
- * Calling #startObserveAllNetworks() to observe all networks. Listening all
- * networks is necessary while the expression of preferred upstreams remains
- * a list of legacy connectivity types. In future, this can be revisited.
- * Calling #registerMobileNetworkRequest() to bring up mobile DUN/HIPRI network.
- *
- * The methods and data members of this class are only to be accessed and
- * modified from the tethering main state machine thread. Any other
- * access semantics would necessitate the addition of locking.
- *
- * TODO: Move upstream selection logic here.
- *
- * All callback methods are run on the same thread as the specified target
- * state machine. This class does not require locking when accessed from this
- * thread. Access from other threads is not advised.
- *
- * @hide
- */
-public class UpstreamNetworkMonitor {
- private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- public static final int EVENT_ON_CAPABILITIES = 1;
- public static final int EVENT_ON_LINKPROPERTIES = 2;
- public static final int EVENT_ON_LOST = 3;
- public static final int NOTIFY_LOCAL_PREFIXES = 10;
- // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default
- // disabled.
- @VisibleForTesting
- public static final int TYPE_NONE = -1;
-
- private static final int CALLBACK_LISTEN_ALL = 1;
- private static final int CALLBACK_DEFAULT_INTERNET = 2;
- private static final int CALLBACK_MOBILE_REQUEST = 3;
-
- private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray();
- static {
- sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR);
- sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR);
- sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR);
- sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI);
- sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH);
- sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET);
- }
-
- private final Context mContext;
- private final SharedLog mLog;
- private final StateMachine mTarget;
- private final Handler mHandler;
- private final int mWhat;
- private final HashMap<Network, UpstreamNetworkState> mNetworkMap = new HashMap<>();
- private HashSet<IpPrefix> mLocalPrefixes;
- private ConnectivityManager mCM;
- private EntitlementManager mEntitlementMgr;
- private NetworkCallback mListenAllCallback;
- private NetworkCallback mDefaultNetworkCallback;
- private NetworkCallback mMobileNetworkCallback;
- private boolean mDunRequired;
- // Whether the current default upstream is mobile or not.
- private boolean mIsDefaultCellularUpstream;
- // The current system default network (not really used yet).
- private Network mDefaultInternetNetwork;
- // The current upstream network used for tethering.
- private Network mTetheringUpstreamNetwork;
-
- public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
- mContext = ctx;
- mTarget = tgt;
- mHandler = mTarget.getHandler();
- mLog = log.forSubComponent(TAG);
- mWhat = what;
- mLocalPrefixes = new HashSet<>();
- mIsDefaultCellularUpstream = false;
- }
-
- @VisibleForTesting
- public UpstreamNetworkMonitor(
- ConnectivityManager cm, StateMachine tgt, SharedLog log, int what) {
- this((Context) null, tgt, log, what);
- mCM = cm;
- }
-
- /**
- * Tracking the system default network. This method should be called when system is ready.
- *
- * @param defaultNetworkRequest should be the same as ConnectivityService default request
- * @param entitle a EntitlementManager object to communicate between EntitlementManager and
- * UpstreamNetworkMonitor
- */
- public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest,
- EntitlementManager entitle) {
-
- // defaultNetworkRequest is not really a "request", just a way of tracking the system
- // default network. It's guaranteed not to actually bring up any networks because it's
- // the should be the same request as the ConnectivityService default request, and thus
- // shares fate with it. We can't use registerDefaultNetworkCallback because it will not
- // track the system default network if there is a VPN that applies to our UID.
- if (mDefaultNetworkCallback == null) {
- mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
- cm().requestNetwork(defaultNetworkRequest, mDefaultNetworkCallback, mHandler);
- }
- if (mEntitlementMgr == null) {
- mEntitlementMgr = entitle;
- }
- }
-
- /** Listen all networks. */
- public void startObserveAllNetworks() {
- stop();
-
- final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
- .clearCapabilities().build();
- mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
- cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
- }
-
- /**
- * Stop tracking candidate tethering upstreams and release mobile network request.
- * Note: this function is used when tethering is stopped because tethering do not need to
- * choose upstream anymore. But it would not stop default network tracking because
- * EntitlementManager may need to know default network to decide whether to request entitlement
- * check even tethering is not active yet.
- */
- public void stop() {
- releaseMobileNetworkRequest();
-
- releaseCallback(mListenAllCallback);
- mListenAllCallback = null;
-
- mTetheringUpstreamNetwork = null;
- mNetworkMap.clear();
- }
-
- /** Setup or teardown DUN connection according to |dunRequired|. */
- public void updateMobileRequiresDun(boolean dunRequired) {
- final boolean valueChanged = (mDunRequired != dunRequired);
- mDunRequired = dunRequired;
- if (valueChanged && mobileNetworkRequested()) {
- releaseMobileNetworkRequest();
- registerMobileNetworkRequest();
- }
- }
-
- /** Whether mobile network is requested. */
- public boolean mobileNetworkRequested() {
- return (mMobileNetworkCallback != null);
- }
-
- /** Request mobile network if mobile upstream is permitted. */
- public void registerMobileNetworkRequest() {
- if (!isCellularUpstreamPermitted()) {
- mLog.i("registerMobileNetworkRequest() is not permitted");
- releaseMobileNetworkRequest();
- return;
- }
- if (mMobileNetworkCallback != null) {
- mLog.e("registerMobileNetworkRequest() already registered");
- return;
- }
-
- final NetworkRequest mobileUpstreamRequest;
- if (mDunRequired) {
- mobileUpstreamRequest = new NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_DUN)
- .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
- .addTransportType(TRANSPORT_CELLULAR).build();
- } else {
- mobileUpstreamRequest = new NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_INTERNET)
- .addTransportType(TRANSPORT_CELLULAR).build();
- }
-
- // The existing default network and DUN callbacks will be notified.
- // Therefore, to avoid duplicate notifications, we only register a no-op.
- mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST);
-
- // The following use of the legacy type system cannot be removed until
- // upstream selection no longer finds networks by legacy type.
- // See also http://b/34364553 .
- final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
-
- // TODO: Change the timeout from 0 (no onUnavailable callback) to some
- // moderate callback timeout. This might be useful for updating some UI.
- // Additionally, we log a message to aid in any subsequent debugging.
- mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
-
- cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler,
- mMobileNetworkCallback);
- }
-
- /** Release mobile network request. */
- public void releaseMobileNetworkRequest() {
- if (mMobileNetworkCallback == null) return;
-
- cm().unregisterNetworkCallback(mMobileNetworkCallback);
- mMobileNetworkCallback = null;
- }
-
- // So many TODOs here, but chief among them is: make this functionality an
- // integral part of this class such that whenever a higher priority network
- // becomes available and useful we (a) file a request to keep it up as
- // necessary and (b) change all upstream tracking state accordingly (by
- // passing LinkProperties up to Tethering).
- /**
- * Select the first available network from |perferredTypes|.
- */
- public UpstreamNetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
- final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
- mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
-
- mLog.log("preferred upstream type: " + typeStatePair.type);
-
- switch (typeStatePair.type) {
- case TYPE_MOBILE_DUN:
- case TYPE_MOBILE_HIPRI:
- // Tethering just selected mobile upstream in spite of the default network being
- // not mobile. This can happen because of the priority list.
- // Notify EntitlementManager to check permission for using mobile upstream.
- if (!mIsDefaultCellularUpstream) {
- mEntitlementMgr.maybeRunProvisioning();
- }
- // If we're on DUN, put our own grab on it.
- registerMobileNetworkRequest();
- break;
- case TYPE_NONE:
- // If we found NONE and mobile upstream is permitted we don't want to do this
- // as we want any previous requests to keep trying to bring up something we can use.
- if (!isCellularUpstreamPermitted()) releaseMobileNetworkRequest();
- break;
- default:
- // If we've found an active upstream connection that's not DUN/HIPRI
- // we should stop any outstanding DUN/HIPRI requests.
- releaseMobileNetworkRequest();
- break;
- }
-
- return typeStatePair.ns;
- }
-
- /**
- * Get current preferred upstream network. If default network is cellular and DUN is required,
- * preferred upstream would be DUN otherwise preferred upstream is the same as default network.
- * Returns null if no current upstream is available.
- */
- public UpstreamNetworkState getCurrentPreferredUpstream() {
- final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
- ? mNetworkMap.get(mDefaultInternetNetwork)
- : null;
- if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
-
- if (!isCellularUpstreamPermitted()) return null;
-
- if (!mDunRequired) return dfltState;
-
- // Find a DUN network. Note that code in Tethering causes a DUN request
- // to be filed, but this might be moved into this class in future.
- return findFirstDunNetwork(mNetworkMap.values());
- }
-
- /** Tell UpstreamNetworkMonitor which network is the current upstream of tethering. */
- public void setCurrentUpstream(Network upstream) {
- mTetheringUpstreamNetwork = upstream;
- }
-
- /** Return local prefixes. */
- public Set<IpPrefix> getLocalPrefixes() {
- return (Set<IpPrefix>) mLocalPrefixes.clone();
- }
-
- private boolean isCellularUpstreamPermitted() {
- if (mEntitlementMgr != null) {
- return mEntitlementMgr.isCellularUpstreamPermitted();
- } else {
- // This flow should only happens in testing.
- return true;
- }
- }
-
- private void handleAvailable(Network network) {
- if (mNetworkMap.containsKey(network)) return;
-
- if (VDBG) Log.d(TAG, "onAvailable for " + network);
- mNetworkMap.put(network, new UpstreamNetworkState(null, null, network));
- }
-
- private void handleNetCap(Network network, NetworkCapabilities newNc) {
- final UpstreamNetworkState prev = mNetworkMap.get(network);
- if (prev == null || newNc.equals(prev.networkCapabilities)) {
- // Ignore notifications about networks for which we have not yet
- // received onAvailable() (should never happen) and any duplicate
- // notifications (e.g. matching more than one of our callbacks).
- return;
- }
-
- if (VDBG) {
- Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
- network, newNc));
- }
-
- mNetworkMap.put(network, new UpstreamNetworkState(
- prev.linkProperties, newNc, network));
- // TODO: If sufficient information is available to select a more
- // preferable upstream, do so now and notify the target.
- notifyTarget(EVENT_ON_CAPABILITIES, network);
- }
-
- private void handleLinkProp(Network network, LinkProperties newLp) {
- final UpstreamNetworkState prev = mNetworkMap.get(network);
- if (prev == null || newLp.equals(prev.linkProperties)) {
- // Ignore notifications about networks for which we have not yet
- // received onAvailable() (should never happen) and any duplicate
- // notifications (e.g. matching more than one of our callbacks).
- return;
- }
-
- if (VDBG) {
- Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
- network, newLp));
- }
-
- mNetworkMap.put(network, new UpstreamNetworkState(
- newLp, prev.networkCapabilities, network));
- // TODO: If sufficient information is available to select a more
- // preferable upstream, do so now and notify the target.
- notifyTarget(EVENT_ON_LINKPROPERTIES, network);
- }
-
- private void handleLost(Network network) {
- // There are few TODOs within ConnectivityService's rematching code
- // pertaining to spurious onLost() notifications.
- //
- // TODO: simplify this, probably if favor of code that:
- // - selects a new upstream if mTetheringUpstreamNetwork has
- // been lost (by any callback)
- // - deletes the entry from the map only when the LISTEN_ALL
- // callback gets notified.
-
- if (!mNetworkMap.containsKey(network)) {
- // Ignore loss of networks about which we had not previously
- // learned any information or for which we have already processed
- // an onLost() notification.
- return;
- }
-
- if (VDBG) Log.d(TAG, "EVENT_ON_LOST for " + network);
-
- // TODO: If sufficient information is available to select a more
- // preferable upstream, do so now and notify the target. Likewise,
- // if the current upstream network is gone, notify the target of the
- // fact that we now have no upstream at all.
- notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
- }
-
- private void recomputeLocalPrefixes() {
- final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
- if (!mLocalPrefixes.equals(localPrefixes)) {
- mLocalPrefixes = localPrefixes;
- notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone());
- }
- }
-
- // Fetch (and cache) a ConnectivityManager only if and when we need one.
- private ConnectivityManager cm() {
- if (mCM == null) {
- // MUST call the String variant to be able to write unittests.
- mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- }
- return mCM;
- }
-
- /**
- * A NetworkCallback class that handles information of interest directly
- * in the thread on which it is invoked. To avoid locking, this MUST be
- * run on the same thread as the target state machine's handler.
- */
- private class UpstreamNetworkCallback extends NetworkCallback {
- private final int mCallbackType;
-
- UpstreamNetworkCallback(int callbackType) {
- mCallbackType = callbackType;
- }
-
- @Override
- public void onAvailable(Network network) {
- handleAvailable(network);
- }
-
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
- if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
- mDefaultInternetNetwork = network;
- final boolean newIsCellular = isCellular(newNc);
- if (mIsDefaultCellularUpstream != newIsCellular) {
- mIsDefaultCellularUpstream = newIsCellular;
- mEntitlementMgr.notifyUpstream(newIsCellular);
- }
- return;
- }
-
- handleNetCap(network, newNc);
- }
-
- @Override
- public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
- if (mCallbackType == CALLBACK_DEFAULT_INTERNET) return;
-
- handleLinkProp(network, newLp);
- // Any non-LISTEN_ALL callback will necessarily concern a network that will
- // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
- // So it's not useful to do this work for non-LISTEN_ALL callbacks.
- if (mCallbackType == CALLBACK_LISTEN_ALL) {
- recomputeLocalPrefixes();
- }
- }
-
- @Override
- public void onLost(Network network) {
- if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
- mDefaultInternetNetwork = null;
- mIsDefaultCellularUpstream = false;
- mEntitlementMgr.notifyUpstream(false);
- return;
- }
-
- handleLost(network);
- // Any non-LISTEN_ALL callback will necessarily concern a network that will
- // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
- // So it's not useful to do this work for non-LISTEN_ALL callbacks.
- if (mCallbackType == CALLBACK_LISTEN_ALL) {
- recomputeLocalPrefixes();
- }
- }
- }
-
- private void releaseCallback(NetworkCallback cb) {
- if (cb != null) cm().unregisterNetworkCallback(cb);
- }
-
- private void notifyTarget(int which, Network network) {
- notifyTarget(which, mNetworkMap.get(network));
- }
-
- private void notifyTarget(int which, Object obj) {
- mTarget.sendMessage(mWhat, which, 0, obj);
- }
-
- private static class TypeStatePair {
- public int type = TYPE_NONE;
- public UpstreamNetworkState ns = null;
- }
-
- private static TypeStatePair findFirstAvailableUpstreamByType(
- Iterable<UpstreamNetworkState> netStates, Iterable<Integer> preferredTypes,
- boolean isCellularUpstreamPermitted) {
- final TypeStatePair result = new TypeStatePair();
-
- for (int type : preferredTypes) {
- NetworkCapabilities nc;
- try {
- nc = networkCapabilitiesForType(type);
- } catch (IllegalArgumentException iae) {
- Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + type);
- continue;
- }
- if (!isCellularUpstreamPermitted && isCellular(nc)) {
- continue;
- }
-
- for (UpstreamNetworkState value : netStates) {
- if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
- continue;
- }
-
- result.type = type;
- result.ns = value;
- return result;
- }
- }
-
- return result;
- }
-
- private static HashSet<IpPrefix> allLocalPrefixes(Iterable<UpstreamNetworkState> netStates) {
- final HashSet<IpPrefix> prefixSet = new HashSet<>();
-
- for (UpstreamNetworkState ns : netStates) {
- final LinkProperties lp = ns.linkProperties;
- if (lp == null) continue;
- prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp));
- }
-
- return prefixSet;
- }
-
- private static boolean isCellular(UpstreamNetworkState ns) {
- return (ns != null) && isCellular(ns.networkCapabilities);
- }
-
- private static boolean isCellular(NetworkCapabilities nc) {
- return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR)
- && nc.hasCapability(NET_CAPABILITY_NOT_VPN);
- }
-
- private static boolean hasCapability(UpstreamNetworkState ns, int netCap) {
- return (ns != null) && (ns.networkCapabilities != null)
- && ns.networkCapabilities.hasCapability(netCap);
- }
-
- private static boolean isNetworkUsableAndNotCellular(UpstreamNetworkState ns) {
- return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null)
- && !isCellular(ns.networkCapabilities);
- }
-
- private static UpstreamNetworkState findFirstDunNetwork(
- Iterable<UpstreamNetworkState> netStates) {
- for (UpstreamNetworkState ns : netStates) {
- if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
- }
-
- return null;
- }
-
- /**
- * Given a legacy type (TYPE_WIFI, ...) returns the corresponding NetworkCapabilities instance.
- * This function is used for deprecated legacy type and be disabled by default.
- */
- @VisibleForTesting
- public static NetworkCapabilities networkCapabilitiesForType(int type) {
- final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
-
- // Map from type to transports.
- final int notFound = -1;
- final int transport = sLegacyTypeToTransport.get(type, notFound);
- if (transport == notFound) {
- throw new IllegalArgumentException("unknown legacy type: " + type);
- }
- builder.addTransportType(transport);
-
- if (type == TYPE_MOBILE_DUN) {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- // DUN is restricted network, see NetworkCapabilities#FORCE_RESTRICTED_CAPABILITIES.
- builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
- } else {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- }
- return builder.build();
- }
-}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java
deleted file mode 100644
index bab9f84cf762..000000000000
--- a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java
+++ /dev/null
@@ -1,51 +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.networkstack.tethering;
-
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-
-import androidx.annotation.NonNull;
-
-/**
- * Snapshot of tethering upstream network state.
- */
-public class UpstreamNetworkState {
- /** {@link LinkProperties}. */
- public final LinkProperties linkProperties;
- /** {@link NetworkCapabilities}. */
- public final NetworkCapabilities networkCapabilities;
- /** {@link Network}. */
- public final Network network;
-
- /** Constructs a new UpstreamNetworkState. */
- public UpstreamNetworkState(LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities, Network network) {
- this.linkProperties = linkProperties;
- this.networkCapabilities = networkCapabilities;
- this.network = network;
- }
-
- @NonNull
- @Override
- public String toString() {
- return String.format("UpstreamNetworkState{%s, %s, %s}",
- network == null ? "null" : network,
- networkCapabilities == null ? "null" : networkCapabilities,
- linkProperties == null ? "null" : linkProperties);
- }
-}
diff --git a/packages/Tethering/tests/Android.bp b/packages/Tethering/tests/Android.bp
deleted file mode 100644
index cb0a20bdf0e8..000000000000
--- a/packages/Tethering/tests/Android.bp
+++ /dev/null
@@ -1,23 +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.
-//
-
-filegroup {
- name: "TetheringTestsJarJarRules",
- srcs: ["jarjar-rules.txt"],
- visibility: [
- "//frameworks/base/packages/Tethering/tests:__subpackages__",
- ]
-}
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
deleted file mode 100644
index 5765c01c43f3..000000000000
--- a/packages/Tethering/tests/integration/Android.bp
+++ /dev/null
@@ -1,85 +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.
-//
-java_defaults {
- name: "TetheringIntegrationTestsDefaults",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
- static_libs: [
- "NetworkStackApiStableLib",
- "androidx.test.rules",
- "mockito-target-extended-minus-junit4",
- "net-tests-utils",
- "testables",
- ],
- libs: [
- "android.test.runner",
- "android.test.base",
- "android.test.mock",
- ],
- jni_libs: [
- // For mockito extended
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
- jarjar_rules: ":NetworkStackJarJarRules",
-}
-
-android_library {
- name: "TetheringIntegrationTestsLib",
- platform_apis: true,
- defaults: ["TetheringIntegrationTestsDefaults"],
- visibility: ["//cts/tests/tests/tethering"]
-}
-
-android_test {
- name: "TetheringIntegrationTests",
- platform_apis: true,
- defaults: ["TetheringIntegrationTestsDefaults"],
- test_suites: [
- "device-tests",
- "mts",
- ],
- compile_multilib: "both",
-}
-
-// Special version of the tethering tests that includes all tests necessary for code coverage
-// purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and
-// NetworkStackTests.
-android_test {
- name: "TetheringCoverageTests",
- platform_apis: true,
- test_suites: ["device-tests", "mts"],
- test_config: "AndroidTest_Coverage.xml",
- defaults: ["libnetworkstackutilsjni_deps"],
- static_libs: [
- "NetworkStaticLibTestsLib",
- "NetworkStackTestsLib",
- "TetheringTestsLib",
- "TetheringIntegrationTestsLib",
- ],
- jni_libs: [
- // For mockito extended
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- // For NetworkStackUtils included in NetworkStackBase
- "libnetworkstackutilsjni",
- ],
- jarjar_rules: ":TetheringTestsJarJarRules",
- compile_multilib: "both",
- manifest: "AndroidManifest_coverage.xml",
-}
diff --git a/packages/Tethering/tests/integration/AndroidManifest.xml b/packages/Tethering/tests/integration/AndroidManifest.xml
deleted file mode 100644
index fddfaad29f0f..000000000000
--- a/packages/Tethering/tests/integration/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack.tethering.tests.integration">
-
- <uses-permission android:name="android.permission.INTERNET"/>
-
- <application android:debuggable="true">
- <uses-library android:name="android.test.runner" />
- </application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.networkstack.tethering.tests.integration"
- android:label="Tethering integration tests">
- </instrumentation>
-</manifest>
diff --git a/packages/Tethering/tests/integration/AndroidManifest_coverage.xml b/packages/Tethering/tests/integration/AndroidManifest_coverage.xml
deleted file mode 100644
index 06de00d78558..000000000000
--- a/packages/Tethering/tests/integration/AndroidManifest_coverage.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.networkstack.tethering.tests.coverage">
-
- <application tools:replace="android:label"
- android:debuggable="true"
- android:label="Tethering coverage tests">
- <uses-library android:name="android.test.runner" />
- </application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.networkstack.tethering.tests.coverage"
- android:label="Tethering coverage tests">
- </instrumentation>
-</manifest>
diff --git a/packages/Tethering/tests/integration/AndroidTest_Coverage.xml b/packages/Tethering/tests/integration/AndroidTest_Coverage.xml
deleted file mode 100644
index 3def2099e45f..000000000000
--- a/packages/Tethering/tests/integration/AndroidTest_Coverage.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<configuration description="Runs coverage tests for Tethering">
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="TetheringCoverageTests.apk" />
- </target_preparer>
-
- <option name="test-tag" value="TetheringCoverageTests" />
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.networkstack.tethering.tests.coverage" />
- <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
- <option name="hidden-api-checks" value="false"/>
- </test>
-</configuration> \ No newline at end of file
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
deleted file mode 100644
index d206ea0b4d45..000000000000
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.Manifest.permission.NETWORK_SETTINGS;
-import static android.Manifest.permission.TETHER_PRIVILEGED;
-import static android.net.TetheringManager.TETHERING_ETHERNET;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.UiAutomation;
-import android.content.Context;
-import android.net.EthernetManager.TetheredInterfaceCallback;
-import android.net.EthernetManager.TetheredInterfaceRequest;
-import android.net.TetheringManager.StartTetheringCallback;
-import android.net.TetheringManager.TetheringEventCallback;
-import android.net.TetheringManager.TetheringRequest;
-import android.net.dhcp.DhcpAckPacket;
-import android.net.dhcp.DhcpOfferPacket;
-import android.net.dhcp.DhcpPacket;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.system.Os;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.testutils.HandlerUtils;
-import com.android.testutils.TapPacketReader;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.FileDescriptor;
-import java.net.Inet4Address;
-import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class EthernetTetheringTest {
-
- private static final String TAG = EthernetTetheringTest.class.getSimpleName();
- private static final int TIMEOUT_MS = 5000;
- private static final int PACKET_READ_TIMEOUT_MS = 100;
- private static final int DHCP_DISCOVER_ATTEMPTS = 10;
- private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
- DhcpPacket.DHCP_SUBNET_MASK,
- DhcpPacket.DHCP_ROUTER,
- DhcpPacket.DHCP_DNS_SERVER,
- DhcpPacket.DHCP_LEASE_TIME,
- };
- private static final String DHCP_HOSTNAME = "testhostname";
-
- private final Context mContext = InstrumentationRegistry.getContext();
- private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class);
- private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class);
-
- private TestNetworkInterface mTestIface;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private TapPacketReader mTapPacketReader;
-
- private TetheredInterfaceRequester mTetheredInterfaceRequester;
- private MyTetheringEventCallback mTetheringEventCallback;
-
- private UiAutomation mUiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
- private boolean mRunTests;
-
- @Before
- public void setUp() throws Exception {
- // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
- // tethered client callbacks.
- mUiAutomation.adoptShellPermissionIdentity(
- MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED);
- mRunTests = mTm.isTetheringSupported() && mEm != null;
- assumeTrue(mRunTests);
-
- mHandlerThread = new HandlerThread(getClass().getSimpleName());
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
- mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
- }
-
- private void cleanUp() throws Exception {
- mTm.stopTethering(TETHERING_ETHERNET);
- if (mTetheringEventCallback != null) {
- mTetheringEventCallback.awaitInterfaceUntethered();
- mTetheringEventCallback.unregister();
- mTetheringEventCallback = null;
- }
- if (mTapPacketReader != null) {
- TapPacketReader reader = mTapPacketReader;
- mHandler.post(() -> reader.stop());
- mTapPacketReader = null;
- }
- mHandlerThread.quitSafely();
- mTetheredInterfaceRequester.release();
- mEm.setIncludeTestInterfaces(false);
- maybeDeleteTestInterface();
- }
-
- @After
- public void tearDown() throws Exception {
- try {
- if (mRunTests) cleanUp();
- } finally {
- mUiAutomation.dropShellPermissionIdentity();
- }
- }
-
- @Test
- public void testVirtualEthernetAlreadyExists() throws Exception {
- // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
- assumeFalse(mEm.isAvailable());
-
- mTestIface = createTestInterface();
- // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the
- // interface will be placed in client mode, which will delete the link-local address.
- // At that point NetworkInterface.getByName() will cease to work on the interface, because
- // starting in R NetworkInterface can no longer see interfaces without IP addresses.
- int mtu = getMTU(mTestIface);
-
- Log.d(TAG, "Including test interfaces");
- mEm.setIncludeTestInterfaces(true);
-
- final String iface = mTetheredInterfaceRequester.getInterface();
- assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
-
- checkVirtualEthernet(mTestIface, mtu);
- }
-
- @Test
- public void testVirtualEthernet() throws Exception {
- // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
- assumeFalse(mEm.isAvailable());
-
- CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface();
-
- mEm.setIncludeTestInterfaces(true);
-
- mTestIface = createTestInterface();
-
- final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
- assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
-
- checkVirtualEthernet(mTestIface, getMTU(mTestIface));
- }
-
- @Test
- public void testStaticIpv4() throws Exception {
- assumeFalse(mEm.isAvailable());
-
- mEm.setIncludeTestInterfaces(true);
-
- mTestIface = createTestInterface();
-
- final String iface = mTetheredInterfaceRequester.getInterface();
- assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
-
- assertInvalidStaticIpv4Request(iface, null, null);
- assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
- assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28");
- assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28");
- assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null);
- assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28");
- assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28");
-
- final String localAddr = "192.0.2.3/28";
- final String clientAddr = "192.0.2.2/28";
- mTetheringEventCallback = enableEthernetTethering(iface,
- requestWithStaticIpv4(localAddr, clientAddr));
-
- mTetheringEventCallback.awaitInterfaceTethered();
- assertInterfaceHasIpAddress(iface, localAddr);
-
- byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
- byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
-
- FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor();
- mTapPacketReader = makePacketReader(fd, getMTU(mTestIface));
- DhcpResults dhcpResults = runDhcp(fd, client1);
- assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
-
- try {
- runDhcp(fd, client2);
- fail("Only one client should get an IP address");
- } catch (TimeoutException expected) { }
-
- }
-
- private boolean isAdbOverNetwork() {
- // If adb TCP port opened, this test may running by adb over network.
- return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
- || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
- }
-
- @Test
- public void testPhysicalEthernet() throws Exception {
- assumeTrue(mEm.isAvailable());
- // Do not run this test if adb is over network and ethernet is connected.
- // It is likely the adb run over ethernet, the adb would break when ethernet is switching
- // from client mode to server mode. See b/160389275.
- assumeFalse(isAdbOverNetwork());
-
- // Get an interface to use.
- final String iface = mTetheredInterfaceRequester.getInterface();
-
- // Enable Ethernet tethering and check that it starts.
- mTetheringEventCallback = enableEthernetTethering(iface);
-
- // There is nothing more we can do on a physical interface without connecting an actual
- // client, which is not possible in this test.
- }
-
- private static final class MyTetheringEventCallback implements TetheringEventCallback {
- private final TetheringManager mTm;
- private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1);
- private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1);
- private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
- private final String mIface;
-
- private volatile boolean mInterfaceWasTethered = false;
- private volatile boolean mUnregistered = false;
- private volatile Collection<TetheredClient> mClients = null;
-
- MyTetheringEventCallback(TetheringManager tm, String iface) {
- mTm = tm;
- mIface = iface;
- }
-
- public void unregister() {
- mTm.unregisterTetheringEventCallback(this);
- mUnregistered = true;
- }
-
- @Override
- public void onTetheredInterfacesChanged(List<String> interfaces) {
- // Ignore stale callbacks registered by previous test cases.
- if (mUnregistered) return;
-
- final boolean wasTethered = mTetheringStartedLatch.getCount() == 0;
- if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) {
- // This interface is being tethered for the first time.
- Log.d(TAG, "Tethering started: " + interfaces);
- mInterfaceWasTethered = true;
- mTetheringStartedLatch.countDown();
- } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) {
- Log.d(TAG, "Tethering stopped: " + interfaces);
- mTetheringStoppedLatch.countDown();
- }
- }
-
- public void awaitInterfaceTethered() throws Exception {
- assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms",
- mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- }
-
- public void awaitInterfaceUntethered() throws Exception {
- // Don't block teardown if the interface was never tethered.
- // This is racy because the interface might become tethered right after this check, but
- // that can only happen in tearDown if startTethering timed out, which likely means
- // the test has already failed.
- if (!mInterfaceWasTethered) return;
-
- assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms",
- mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- }
-
- @Override
- public void onError(String ifName, int error) {
- // Ignore stale callbacks registered by previous test cases.
- if (mUnregistered) return;
-
- fail("TetheringEventCallback got error:" + error + " on iface " + ifName);
- }
-
- @Override
- public void onClientsChanged(Collection<TetheredClient> clients) {
- // Ignore stale callbacks registered by previous test cases.
- if (mUnregistered) return;
-
- Log.d(TAG, "Got clients changed: " + clients);
- mClients = clients;
- if (clients.size() > 0) {
- mClientConnectedLatch.countDown();
- }
- }
-
- public Collection<TetheredClient> awaitClientConnected() throws Exception {
- assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms",
- mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- return mClients;
- }
- }
-
- private MyTetheringEventCallback enableEthernetTethering(String iface,
- TetheringRequest request) throws Exception {
- MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface);
- mTm.registerTetheringEventCallback(mHandler::post, callback);
-
- StartTetheringCallback startTetheringCallback = new StartTetheringCallback() {
- @Override
- public void onTetheringFailed(int resultCode) {
- fail("Unexpectedly got onTetheringFailed");
- }
- };
- Log.d(TAG, "Starting Ethernet tethering");
- mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback);
- callback.awaitInterfaceTethered();
- return callback;
- }
-
- private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
- return enableEthernetTethering(iface,
- new TetheringRequest.Builder(TETHERING_ETHERNET)
- .setShouldShowEntitlementUi(false).build());
- }
-
- private int getMTU(TestNetworkInterface iface) throws SocketException {
- NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName());
- assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif);
- return nif.getMTU();
- }
-
- private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
- final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
- mHandler.post(() -> reader.start());
- HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
- return reader;
- }
-
- private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
- FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
- mTapPacketReader = makePacketReader(fd, mtu);
- mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName());
- checkTetheredClientCallbacks(fd);
- }
-
- private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception {
- // We have to retransmit DHCP requests because IpServer declares itself to be ready before
- // its DhcpServer is actually started. TODO: fix this race and remove this loop.
- DhcpPacket offerPacket = null;
- for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) {
- Log.d(TAG, "Sending DHCP discover");
- sendDhcpDiscover(fd, clientMacAddr);
- offerPacket = getNextDhcpPacket();
- if (offerPacket instanceof DhcpOfferPacket) break;
- }
- if (!(offerPacket instanceof DhcpOfferPacket)) {
- throw new TimeoutException("No DHCPOFFER received on interface within timeout");
- }
-
- sendDhcpRequest(fd, offerPacket, clientMacAddr);
- DhcpPacket ackPacket = getNextDhcpPacket();
- if (!(ackPacket instanceof DhcpAckPacket)) {
- throw new TimeoutException("No DHCPACK received on interface within timeout");
- }
-
- return ackPacket.toDhcpResults();
- }
-
- private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
- // Create a fake client.
- byte[] clientMacAddr = new byte[6];
- new Random().nextBytes(clientMacAddr);
-
- DhcpResults dhcpResults = runDhcp(fd, clientMacAddr);
-
- final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
- assertEquals(1, clients.size());
- final TetheredClient client = clients.iterator().next();
-
- // Check the MAC address.
- assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress());
- assertEquals(TETHERING_ETHERNET, client.getTetheringType());
-
- // Check the hostname.
- assertEquals(1, client.getAddresses().size());
- TetheredClient.AddressInfo info = client.getAddresses().get(0);
- assertEquals(DHCP_HOSTNAME, info.getHostname());
-
- // Check the address is the one that was handed out in the DHCP ACK.
- assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
-
- // Check that the lifetime is correct +/- 10s.
- final long now = SystemClock.elapsedRealtime();
- final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000;
- final String msg = String.format("IP address should have lifetime of %d, got %d",
- dhcpResults.leaseDuration, actualLeaseDuration);
- assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10);
- }
-
- private DhcpPacket getNextDhcpPacket() throws ParseException {
- byte[] packet;
- while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) {
- try {
- return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2);
- } catch (DhcpPacket.ParseException e) {
- // Not a DHCP packet. Continue.
- }
- }
- return null;
- }
-
- private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback {
- private final CountDownLatch mInterfaceAvailableLatch = new CountDownLatch(1);
- private final Handler mHandler;
- private final EthernetManager mEm;
-
- private TetheredInterfaceRequest mRequest;
- private final CompletableFuture<String> mFuture = new CompletableFuture<>();
-
- TetheredInterfaceRequester(Handler handler, EthernetManager em) {
- mHandler = handler;
- mEm = em;
- }
-
- @Override
- public void onAvailable(String iface) {
- Log.d(TAG, "Ethernet interface available: " + iface);
- mFuture.complete(iface);
- }
-
- @Override
- public void onUnavailable() {
- mFuture.completeExceptionally(new IllegalStateException("onUnavailable received"));
- }
-
- public CompletableFuture<String> requestInterface() {
- assertNull("BUG: more than one tethered interface request", mRequest);
- Log.d(TAG, "Requesting tethered interface");
- mRequest = mEm.requestTetheredInterface(mHandler::post, this);
- return mFuture;
- }
-
- public String getInterface() throws Exception {
- return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
- }
-
- public void release() {
- if (mRequest != null) {
- mFuture.obtrudeException(new IllegalStateException("Request already released"));
- mRequest.release();
- mRequest = null;
- }
- }
- }
-
- private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception {
- ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2,
- new Random().nextInt() /* transactionId */, (short) 0 /* secs */,
- macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS,
- false /* rapid commit */, DHCP_HOSTNAME);
- sendPacket(fd, packet);
- }
-
- private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress)
- throws Exception {
- DhcpResults results = offerPacket.toDhcpResults();
- Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress();
- Inet4Address serverIdentifier = results.serverAddress;
- ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2,
- 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */,
- false /* broadcast */, macAddress, clientIp /* requestedIpAddress */,
- serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME);
- sendPacket(fd, packet);
- }
-
- private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception {
- assertNotNull("Only tests on virtual interfaces can send packets", fd);
- Os.write(fd, packet);
- }
-
- public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) {
- // Check all fields except the deprecation and expiry times.
- String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2);
- assertTrue(msg, l1.isSameAddressAs(l2));
- assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags());
- assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope());
- }
-
- private TetheringRequest requestWithStaticIpv4(String local, String client) {
- LinkAddress localAddr = local == null ? null : new LinkAddress(local);
- LinkAddress clientAddr = client == null ? null : new LinkAddress(client);
- return new TetheringRequest.Builder(TETHERING_ETHERNET)
- .setStaticIpv4Addresses(localAddr, clientAddr)
- .setShouldShowEntitlementUi(false).build();
- }
-
- private void assertInvalidStaticIpv4Request(String iface, String local, String client)
- throws Exception {
- try {
- enableEthernetTethering(iface, requestWithStaticIpv4(local, client));
- fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client);
- } catch (IllegalArgumentException | NullPointerException expected) { }
- }
-
- private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception {
- LinkAddress expectedAddr = new LinkAddress(expected);
- NetworkInterface nif = NetworkInterface.getByName(iface);
- for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
- final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
- if (expectedAddr.equals(addr)) {
- return;
- }
- }
- fail("Expected " + iface + " to have IP address " + expected + ", found "
- + nif.getInterfaceAddresses());
- }
-
- private TestNetworkInterface createTestInterface() throws Exception {
- TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
- TestNetworkInterface iface = tnm.createTapInterface();
- Log.d(TAG, "Created test interface " + iface.getInterfaceName());
- return iface;
- }
-
- private void maybeDeleteTestInterface() throws Exception {
- if (mTestIface != null) {
- mTestIface.getFileDescriptor().close();
- Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName());
- mTestIface = null;
- }
- }
-}
diff --git a/packages/Tethering/tests/jarjar-rules.txt b/packages/Tethering/tests/jarjar-rules.txt
deleted file mode 100644
index c99ff7f81877..000000000000
--- a/packages/Tethering/tests/jarjar-rules.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-# Don't jar-jar the entire package because this test use some
-# internal classes (like ArrayUtils in com.android.internal.util)
-rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1
-rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
-rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
-rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
-rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
-rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
-rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
-
-rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
-
-# Classes from net-utils-framework-common
-rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1
-
-# TODO: either stop using frameworks-base-testutils or remove the unit test classes it contains.
-# TestableLooper from "testables" can be used instead of TestLooper from frameworks-base-testutils.
-zap android.os.test.TestLooperTest*
-zap com.android.test.filters.SelectTestTests*
diff --git a/packages/Tethering/tests/mts/Android.bp b/packages/Tethering/tests/mts/Android.bp
deleted file mode 100644
index f925b0a53f32..000000000000
--- a/packages/Tethering/tests/mts/Android.bp
+++ /dev/null
@@ -1,56 +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.
-
-android_test {
- // This tests for functionality that is not required for devices that
- // don't use Tethering mainline module.
- name: "MtsTetheringTest",
-
- libs: [
- "android.test.base",
- ],
-
- srcs: [
- "src/**/*.java",
- ],
-
- static_libs: [
- "androidx.test.rules",
- // mockito-target-extended-minus-junit4 used in this lib have dependency with
- // jni_libs libdexmakerjvmtiagent and libstaticjvmtiagent.
- "cts-net-utils",
- // This is needed for androidx.test.runner.AndroidJUnitRunner.
- "ctstestrunner-axt",
- "junit",
- "junit-params",
- ],
-
- jni_libs: [
- // For mockito extended which is pulled in from -net-utils -> net-tests-utils
- // (mockito-target-extended-minus-junit4).
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
-
- platform_apis: true,
-
- // Tag this module as a mts test artifact
- test_suites: [
- "general-tests",
- "mts",
- ],
-
- // Include both the 32 and 64 bit versions
- compile_multilib: "both",
-}
diff --git a/packages/Tethering/tests/mts/AndroidManifest.xml b/packages/Tethering/tests/mts/AndroidManifest.xml
deleted file mode 100644
index 6d2abcad42a3..000000000000
--- a/packages/Tethering/tests/mts/AndroidManifest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.tethering.mts">
-
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.INTERNET"/>
-
- <application android:debuggable="true">
- <uses-library android:name="android.test.runner" />
- </application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.tethering.mts"
- android:label="MTS tests of android.tethering">
- <meta-data android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
- </instrumentation>
-
-</manifest>
diff --git a/packages/Tethering/tests/mts/AndroidTest.xml b/packages/Tethering/tests/mts/AndroidTest.xml
deleted file mode 100644
index 80788dfa6f40..000000000000
--- a/packages/Tethering/tests/mts/AndroidTest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Config for MTS Tethering test cases">
- <option name="test-suite-tag" value="mts" />
- <option name="config-descriptor:metadata" key="component" value="networking" />
- <!-- Instant app do not have INTERNET permission. -->
- <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
- <!-- Feature is not backed by native code. -->
- <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <!-- Allow running this against a secondary user. -->
- <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="MtsTetheringTest.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.tethering.mts" />
- </test>
-
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.google.android.tethering" />
- </object>
-</configuration>
diff --git a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
deleted file mode 100644
index 7ffe37ad648d..000000000000
--- a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.tethering.mts;
-
-import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.Manifest.permission.NETWORK_SETTINGS;
-import static android.Manifest.permission.READ_DEVICE_CONFIG;
-import static android.Manifest.permission.TETHER_PRIVILEGED;
-import static android.Manifest.permission.WRITE_SETTINGS;
-import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
-import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
-
-import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.UiAutomation;
-import android.content.Context;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.TetheringManager;
-import android.net.cts.util.CtsTetheringUtils;
-import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.NonNull;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.testutils.TestNetworkTracker;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-public class TetheringModuleTest {
- private Context mContext;
- private TetheringManager mTm;
- private CtsTetheringUtils mCtsTetheringUtils;
-
- private UiAutomation mUiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
-
- @Before
- public void setUp() throws Exception {
- mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS,
- WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED);
- mContext = InstrumentationRegistry.getContext();
- mTm = mContext.getSystemService(TetheringManager.class);
- mCtsTetheringUtils = new CtsTetheringUtils(mContext);
- }
-
- @After
- public void tearDown() throws Exception {
- mUiAutomation.dropShellPermissionIdentity();
- }
-
- private static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
- "tether_enable_select_all_prefix_ranges";
- @Test
- public void testSwitchBasePrefixRangeWhenConflict() throws Exception {
- assumeTrue(isFeatureEnabled(TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true));
-
- addressConflictTest(true);
- }
-
- @Test
- public void testSwitchPrefixRangeWhenConflict() throws Exception {
- addressConflictTest(false);
- }
-
- private void addressConflictTest(final boolean wholeRangeConflict) throws Exception {
- final TestTetheringEventCallback tetherEventCallback =
- mCtsTetheringUtils.registerTetheringEventCallback();
-
- TestNetworkTracker tnt = null;
- try {
- tetherEventCallback.assumeTetheringSupported();
- assumeTrue(isWifiTetheringSupported(tetherEventCallback));
-
- mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
-
- final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces();
- assertEquals(1, tetheredIfaces.size());
- final String wifiTetheringIface = tetheredIfaces.get(0);
-
- NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface);
- // Tethering downstream only have one ipv4 address.
- final LinkAddress hotspotAddr = getFirstIpv4Address(nif);
- assertNotNull(hotspotAddr);
-
- final IpPrefix testPrefix = getConflictingPrefix(hotspotAddr, wholeRangeConflict);
- assertNotNull(testPrefix);
-
- tnt = setUpTestNetwork(
- new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength()));
-
- tetherEventCallback.expectTetheredInterfacesChanged(null);
- final List<String> wifiRegexs =
- tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
-
- tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs);
- nif = NetworkInterface.getByName(wifiTetheringIface);
- final LinkAddress newHotspotAddr = getFirstIpv4Address(nif);
- assertNotNull(newHotspotAddr);
-
- assertFalse(testPrefix.containsPrefix(
- new IpPrefix(newHotspotAddr.getAddress(), newHotspotAddr.getPrefixLength())));
-
- mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
- } finally {
- if (tnt != null) {
- tnt.teardown();
- }
- mTm.stopAllTethering();
- mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
- }
- }
-
- private LinkAddress getFirstIpv4Address(final NetworkInterface nif) {
- for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
- final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
- if (addr.isIpv4()) return addr;
- }
- return null;
- }
-
- @NonNull
- private IpPrefix getConflictingPrefix(final LinkAddress address,
- final boolean wholeRangeConflict) {
- if (!wholeRangeConflict) {
- return new IpPrefix(address.getAddress(), address.getPrefixLength());
- }
-
- final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList(
- new IpPrefix("192.168.0.0/16"),
- new IpPrefix("172.16.0.0/12"),
- new IpPrefix("10.0.0.0/8")));
-
- for (IpPrefix prefix : prefixPool) {
- if (prefix.contains(address.getAddress())) return prefix;
- }
-
- fail("Could not find sutiable conflict prefix");
-
- // Never go here.
- return null;
- }
-
- private TestNetworkTracker setUpTestNetwork(final LinkAddress address) throws Exception {
- return initTestNetwork(mContext, address, 10_000L /* test timeout ms*/);
-
- }
-
- public static boolean isFeatureEnabled(final String name, final boolean defaultValue) {
- return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue);
- }
-}
diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp
deleted file mode 100644
index 9217345dc2f5..000000000000
--- a/packages/Tethering/tests/privileged/Android.bp
+++ /dev/null
@@ -1,49 +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.
-//
-
-java_defaults {
- name: "TetheringPrivilegedTestsJniDefaults",
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- "libtetherutilsjni",
- ],
- jni_uses_sdk_apis: true,
- visibility: ["//visibility:private"],
-}
-
-android_test {
- name: "TetheringPrivilegedTests",
- defaults: [
- "TetheringPrivilegedTestsJniDefaults",
- ],
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
- certificate: "networkstack",
- platform_apis: true,
- test_suites: [
- "device-tests",
- "mts",
- ],
- static_libs: [
- "androidx.test.rules",
- "net-tests-utils",
- "TetheringApiCurrentLib",
- ],
- compile_multilib: "both",
-}
diff --git a/packages/Tethering/tests/privileged/AndroidManifest.xml b/packages/Tethering/tests/privileged/AndroidManifest.xml
deleted file mode 100644
index 49eba15d13d4..000000000000
--- a/packages/Tethering/tests/privileged/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack.tethering.tests.privileged"
- android:sharedUserId="android.uid.networkstack">
-
- <!-- Note: do not add any privileged or signature permissions that are granted
- to the network stack and its shared uid apps. Otherwise, the test APK will
- install, but when the device is rebooted, it will bootloop because this
- test APK is not in the privileged permission allow list -->
-
- <application android:debuggable="true">
- <uses-library android:name="android.test.runner" />
- </application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.networkstack.tethering.tests.privileged"
- android:label="Tethering privileged tests">
- </instrumentation>
-</manifest>
diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
deleted file mode 100644
index 42a91aa9acd5..000000000000
--- a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-
-import static com.android.net.module.util.IpUtils.icmpv6Checksum;
-import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Instrumentation;
-import android.content.Context;
-import android.net.INetd;
-import android.net.InetAddresses;
-import android.net.MacAddress;
-import android.net.util.InterfaceParams;
-import android.net.util.TetheringUtils;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.testutils.TapPacketReader;
-import com.android.testutils.TapPacketReaderRule;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-
-import java.nio.ByteBuffer;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class DadProxyTest {
- private static final int DATA_BUFFER_LEN = 4096;
- private static final int PACKET_TIMEOUT_MS = 5_000;
-
- // Start the readers manually on a common handler shared with DadProxy, for simplicity
- @Rule
- public final TapPacketReaderRule mUpstreamReader = new TapPacketReaderRule(
- DATA_BUFFER_LEN, false /* autoStart */);
- @Rule
- public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule(
- DATA_BUFFER_LEN, false /* autoStart */);
-
- private InterfaceParams mUpstreamParams, mTetheredParams;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader;
-
- private static INetd sNetd;
-
- @BeforeClass
- public static void setupOnce() {
- System.loadLibrary("tetherutilsjni");
-
- final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
- final IBinder netdIBinder =
- (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
- sNetd = INetd.Stub.asInterface(netdIBinder);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mHandlerThread = new HandlerThread(getClass().getSimpleName());
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
-
- setupTapInterfaces();
-
- // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads.
- if (Looper.myLooper() == null) Looper.prepare();
-
- DadProxy mProxy = setupProxy();
- }
-
- @After
- public void tearDown() throws Exception {
- mUpstreamReader.stop();
- mTetheredReader.stop();
-
- if (mHandlerThread != null) {
- mHandlerThread.quitSafely();
- mHandlerThread.join(PACKET_TIMEOUT_MS);
- }
-
- if (mTetheredParams != null) {
- sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
- }
- if (mUpstreamParams != null) {
- sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
- }
- }
-
- private void setupTapInterfaces() {
- // Create upstream test iface.
- mUpstreamReader.start(mHandler);
- mUpstreamParams = InterfaceParams.getByName(mUpstreamReader.iface.getInterfaceName());
- assertNotNull(mUpstreamParams);
- mUpstreamPacketReader = mUpstreamReader.getReader();
-
- // Create tethered test iface.
- mTetheredReader.start(mHandler);
- mTetheredParams = InterfaceParams.getByName(mTetheredReader.getIface().getInterfaceName());
- assertNotNull(mTetheredParams);
- mTetheredPacketReader = mTetheredReader.getReader();
- }
-
- private static final int IPV6_HEADER_LEN = 40;
- private static final int ETH_HEADER_LEN = 14;
- private static final int ICMPV6_NA_NS_LEN = 24;
- private static final int LL_TARGET_OPTION_LEN = 8;
- private static final int ICMPV6_CHECKSUM_OFFSET = 2;
- private static final int ETHER_TYPE_IPV6 = 0x86dd;
-
- private static ByteBuffer createDadPacket(int type) {
- // Refer to buildArpPacket()
- int icmpLen = ICMPV6_NA_NS_LEN
- + (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT
- ? LL_TARGET_OPTION_LEN : 0);
- final ByteBuffer buf = ByteBuffer.allocate(icmpLen + IPV6_HEADER_LEN + ETH_HEADER_LEN);
-
- // Ethernet header.
- final MacAddress srcMac = MacAddress.fromString("33:33:ff:66:77:88");
- buf.put(srcMac.toByteArray());
- final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06");
- buf.put(dstMac.toByteArray());
- buf.putShort((short) ETHER_TYPE_IPV6);
-
- // IPv6 header
- byte[] version = {(byte) 0x60, 0x00, 0x00, 0x00};
- buf.put(version); // Version
- buf.putShort((byte) icmpLen); // Length
- buf.put((byte) IPPROTO_ICMPV6); // Next header
- buf.put((byte) 0xff); // Hop limit
-
- final byte[] target =
- InetAddresses.parseNumericAddress("fe80::1122:3344:5566:7788").getAddress();
- final byte[] src;
- final byte[] dst;
- if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION) {
- src = InetAddresses.parseNumericAddress("::").getAddress();
- dst = InetAddresses.parseNumericAddress("ff02::1:ff66:7788").getAddress();
- } else {
- src = target;
- dst = TetheringUtils.ALL_NODES;
- }
- buf.put(src);
- buf.put(dst);
-
- // ICMPv6 Header
- buf.put((byte) type); // Type
- buf.put((byte) 0x00); // Code
- buf.putShort((short) 0); // Checksum
- buf.putInt(0); // Reserved
- buf.put(target);
-
- if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT) {
- //NA packet has LL target address
- //ICMPv6 Option
- buf.put((byte) 0x02); // Type
- buf.put((byte) 0x01); // Length
- byte[] ll_target = MacAddress.fromString("01:02:03:04:05:06").toByteArray();
- buf.put(ll_target);
- }
-
- // Populate checksum field
- final int transportOffset = ETH_HEADER_LEN + IPV6_HEADER_LEN;
- final short checksum = icmpv6Checksum(buf, ETH_HEADER_LEN, transportOffset, icmpLen);
- buf.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum);
-
- buf.flip();
- return buf;
- }
-
- private DadProxy setupProxy() throws Exception {
- DadProxy proxy = new DadProxy(mHandler, mTetheredParams);
- mHandler.post(() -> proxy.setUpstreamIface(mUpstreamParams));
-
- // Upstream iface is added to local network to simplify test case.
- // Otherwise the test needs to create and destroy a network for the upstream iface.
- sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
- sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
-
- return proxy;
- }
-
- // TODO: change to assert.
- private boolean waitForPacket(ByteBuffer packet, TapPacketReader reader) {
- byte[] p;
-
- while ((p = reader.popPacket(PACKET_TIMEOUT_MS)) != null) {
- final ByteBuffer buffer = ByteBuffer.wrap(p);
-
- if (buffer.compareTo(packet) == 0) return true;
- }
- return false;
- }
-
- private void updateDstMac(ByteBuffer buf, MacAddress mac) {
- buf.put(mac.toByteArray());
- buf.rewind();
- }
- private void updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams) {
- buf.position(ETHER_SRC_ADDR_OFFSET);
- buf.put(ifaceParams.macAddr.toByteArray());
- buf.rewind();
- }
-
- @Test
- public void testNaForwardingFromUpstreamToTether() throws Exception {
- ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
-
- mUpstreamPacketReader.sendResponse(na);
- updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
- updateSrcMac(na, mTetheredParams);
- assertTrue(waitForPacket(na, mTetheredPacketReader));
- }
-
- @Test
- // TODO: remove test once DAD works in both directions.
- public void testNaForwardingFromTetherToUpstream() throws Exception {
- ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
-
- mTetheredPacketReader.sendResponse(na);
- updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
- updateSrcMac(na, mTetheredParams);
- assertFalse(waitForPacket(na, mUpstreamPacketReader));
- }
-
- @Test
- public void testNsForwardingFromTetherToUpstream() throws Exception {
- ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
-
- mTetheredPacketReader.sendResponse(ns);
- updateSrcMac(ns, mUpstreamParams);
- assertTrue(waitForPacket(ns, mUpstreamPacketReader));
- }
-
- @Test
- // TODO: remove test once DAD works in both directions.
- public void testNsForwardingFromUpstreamToTether() throws Exception {
- ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
-
- mUpstreamPacketReader.sendResponse(ns);
- updateSrcMac(ns, mUpstreamParams);
- assertFalse(waitForPacket(ns, mTetheredPacketReader));
- }
-}
diff --git a/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
deleted file mode 100644
index 57c28fc67cc3..000000000000
--- a/packages/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
+++ /dev/null
@@ -1,130 +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.networkstack.tethering;
-
-import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
-import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
-import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-
-import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_GET;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_NEW;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.NFNL_SUBSYS_CTNETLINK;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_DESTROY;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_NEW;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.net.netlink.StructNlMsgHdr;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.NativeHandle;
-import android.system.Os;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ConntrackSocketTest {
- private static final long TIMEOUT = 500;
-
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private final SharedLog mLog = new SharedLog("privileged-test");
-
- private OffloadHardwareInterface mOffloadHw;
- private OffloadHardwareInterface.Dependencies mDeps;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mHandlerThread = new HandlerThread(getClass().getSimpleName());
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
-
- // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads.
- if (Looper.myLooper() == null) Looper.prepare();
-
- mDeps = new OffloadHardwareInterface.Dependencies(mLog);
- mOffloadHw = new OffloadHardwareInterface(mHandler, mLog, mDeps);
- }
-
- @Test
- public void testIpv4ConntrackSocket() throws Exception {
- // Set up server and connect.
- final InetSocketAddress anyAddress = new InetSocketAddress(
- InetAddress.getByName("127.0.0.1"), 0);
- final ServerSocket serverSocket = new ServerSocket();
- serverSocket.bind(anyAddress);
- final SocketAddress theAddress = serverSocket.getLocalSocketAddress();
-
- // Make a connection to the server.
- final Socket socket = new Socket();
- socket.connect(theAddress);
- final Socket acceptedSocket = serverSocket.accept();
-
- final NativeHandle handle = mDeps.createConntrackSocket(
- NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
- mOffloadHw.sendIpv4NfGenMsg(handle,
- (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
- (short) (NLM_F_REQUEST | NLM_F_DUMP));
-
- boolean foundConntrackEntry = false;
- ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_RECV_BUFSIZE);
- buffer.order(ByteOrder.nativeOrder());
-
- try {
- while (Os.read(handle.getFileDescriptor(), buffer) > 0) {
- buffer.flip();
-
- // TODO: ConntrackMessage should get a parse API like StructNlMsgHdr
- // so we can confirm that the conntrack added is for the TCP connection above.
- final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(buffer);
- assertNotNull(nlmsghdr);
-
- // As long as 1 conntrack entry is found test case will pass, even if it's not
- // the from the TCP connection above.
- if (nlmsghdr.nlmsg_type == ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW)) {
- foundConntrackEntry = true;
- break;
- }
- }
- } finally {
- socket.close();
- serverSocket.close();
- }
- assertTrue("Did not receive any NFNL_SUBSYS_CTNETLINK/IPCTNL_MSG_CT_NEW message",
- foundConntrackEntry);
- }
-}
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
deleted file mode 100644
index 3589725dcf50..000000000000
--- a/packages/Tethering/tests/unit/Android.bp
+++ /dev/null
@@ -1,93 +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.
-//
-
-// Tests in this folder are included both in unit tests and CTS.
-java_library {
- name: "TetheringCommonTests",
- srcs: [
- "common/**/*.java",
- "common/**/*.kt"
- ],
- static_libs: [
- "androidx.test.rules",
- "net-tests-utils",
- ],
- // TODO(b/147200698) change sdk_version to module-current and remove framework-minus-apex
- sdk_version: "core_platform",
- libs: [
- "framework-minus-apex",
- "framework-tethering.impl",
- ],
- visibility: ["//cts/tests/tests/tethering"],
-}
-
-java_defaults {
- name: "TetheringTestsDefaults",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
- static_libs: [
- "TetheringApiCurrentLib",
- "TetheringCommonTests",
- "androidx.test.rules",
- "frameworks-base-testutils",
- "mockito-target-extended-minus-junit4",
- "net-tests-utils",
- "testables",
- ],
- // TODO(b/147200698) change sdk_version to module-current and
- // remove framework-minus-apex, ext, and framework-res
- sdk_version: "core_platform",
- libs: [
- "android.test.runner",
- "android.test.base",
- "android.test.mock",
- "ext",
- "framework-minus-apex",
- "framework-res",
- "framework-tethering.impl",
- "framework-wifi.stubs.module_lib",
- ],
- jni_libs: [
- // For mockito extended
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
-}
-
-// Library containing the unit tests. This is used by the coverage test target to pull in the
-// unit test code. It is not currently used by the tests themselves because all the build
-// configuration needed by the tests is in the TetheringTestsDefaults rule.
-android_library {
- name: "TetheringTestsLib",
- defaults: ["TetheringTestsDefaults"],
- visibility: [
- "//frameworks/base/packages/Tethering/tests/integration",
- ]
-}
-
-android_test {
- name: "TetheringTests",
- platform_apis: true,
- test_suites: [
- "device-tests",
- "mts",
- ],
- jarjar_rules: ":TetheringTestsJarJarRules",
- defaults: ["TetheringTestsDefaults"],
- compile_multilib: "both",
-}
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
deleted file mode 100644
index 355342f64371..000000000000
--- a/packages/Tethering/tests/unit/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack.tethering.tests.unit">
-
- <application android:debuggable="true">
- <uses-library android:name="android.test.runner" />
- <service
- android:name="com.android.networkstack.tethering.MockTetheringService"
- android:permission="android.permission.TETHER_PRIVILEGED"
- android:exported="true">
- <intent-filter>
- <action android:name="com.android.networkstack.tethering.TetheringService"/>
- </intent-filter>
- </service>
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.networkstack.tethering.tests.unit"
- android:label="Tethering service tests">
- </instrumentation>
-</manifest>
diff --git a/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt
deleted file mode 100644
index 55c59dd08f41..000000000000
--- a/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net
-
-import android.net.InetAddresses.parseNumericAddress
-import android.net.TetheredClient.AddressInfo
-import android.net.TetheringManager.TETHERING_BLUETOOTH
-import android.net.TetheringManager.TETHERING_USB
-import android.system.OsConstants.RT_SCOPE_UNIVERSE
-import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
-import org.junit.Test
-import org.junit.runner.RunWith
-import kotlin.test.assertEquals
-import kotlin.test.assertNotEquals
-
-private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67))
-private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78))
-private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L)
-private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L)
-private val TEST_HOSTNAME = "test_hostname"
-private val TEST_OTHER_HOSTNAME = "test_other_hostname"
-private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, TEST_HOSTNAME)
-private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null)
-
-private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress(
- parseNumericAddress(addr),
- prefixLength,
- 0 /* flags */,
- RT_SCOPE_UNIVERSE,
- expTime /* deprecationTime */,
- expTime /* expirationTime */)
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class TetheredClientTest {
- @Test
- fun testParceling() {
- assertParcelSane(TEST_ADDRINFO1, fieldCount = 2)
- assertParcelSane(makeTestClient(), fieldCount = 3)
- }
-
- @Test
- fun testEquals() {
- assertEquals(makeTestClient(), makeTestClient())
-
- // Different mac address
- assertNotEquals(makeTestClient(), TetheredClient(
- TEST_OTHER_MACADDR,
- listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
- TETHERING_BLUETOOTH))
-
- // Different hostname
- assertNotEquals(makeTestClient(), TetheredClient(
- TEST_MACADDR,
- listOf(AddressInfo(TEST_ADDR1, TEST_OTHER_HOSTNAME), TEST_ADDRINFO2),
- TETHERING_BLUETOOTH))
-
- // Null hostname
- assertNotEquals(makeTestClient(), TetheredClient(
- TEST_MACADDR,
- listOf(AddressInfo(TEST_ADDR1, null), TEST_ADDRINFO2),
- TETHERING_BLUETOOTH))
-
- // Missing address
- assertNotEquals(makeTestClient(), TetheredClient(
- TEST_MACADDR,
- listOf(TEST_ADDRINFO2),
- TETHERING_BLUETOOTH))
-
- // Different type
- assertNotEquals(makeTestClient(), TetheredClient(
- TEST_MACADDR,
- listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
- TETHERING_USB))
- }
-
- @Test
- fun testAddAddresses() {
- val client1 = TetheredClient(TEST_MACADDR, listOf(TEST_ADDRINFO1), TETHERING_USB)
- val client2 = TetheredClient(TEST_OTHER_MACADDR, listOf(TEST_ADDRINFO2), TETHERING_USB)
- assertEquals(TetheredClient(
- TEST_MACADDR,
- listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
- TETHERING_USB), client1.addAddresses(client2))
- }
-
- @Test
- fun testGetters() {
- assertEquals(TEST_MACADDR, makeTestClient().macAddress)
- assertEquals(listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), makeTestClient().addresses)
- assertEquals(TETHERING_BLUETOOTH, makeTestClient().tetheringType)
- }
-
- @Test
- fun testAddressInfo_Getters() {
- assertEquals(TEST_ADDR1, TEST_ADDRINFO1.address)
- assertEquals(TEST_ADDR2, TEST_ADDRINFO2.address)
- assertEquals(TEST_HOSTNAME, TEST_ADDRINFO1.hostname)
- assertEquals(null, TEST_ADDRINFO2.hostname)
- }
-
- private fun makeTestClient() = TetheredClient(
- TEST_MACADDR,
- listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
- TETHERING_BLUETOOTH)
-} \ No newline at end of file
diff --git a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
deleted file mode 100644
index a8857b2e5cb0..000000000000
--- a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import static android.net.InetAddresses.parseNumericAddress;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.net.LinkAddress;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.net.Inet4Address;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class DhcpServingParamsParcelExtTest {
- private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123");
- private static final Inet4Address TEST_CLIENT_ADDRESS = inet4Addr("192.168.0.42");
- private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b;
- private static final int TEST_CLIENT_ADDRESS_PARCELED = 0xc0a8002a;
- private static final int TEST_PREFIX_LENGTH = 17;
- private static final int TEST_LEASE_TIME_SECS = 120;
- private static final int TEST_MTU = 1000;
- private static final Set<Inet4Address> TEST_ADDRESS_SET =
- new HashSet<Inet4Address>(Arrays.asList(
- new Inet4Address[] {inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124")}));
- private static final Set<Integer> TEST_ADDRESS_SET_PARCELED =
- new HashSet<Integer>(Arrays.asList(new Integer[] {0xc0a8017b, 0xc0a8017c}));
-
- private DhcpServingParamsParcelExt mParcel;
-
- @Before
- public void setUp() {
- mParcel = new DhcpServingParamsParcelExt();
- }
-
- @Test
- public void testSetServerAddr() {
- mParcel.setServerAddr(new LinkAddress(TEST_ADDRESS, TEST_PREFIX_LENGTH));
-
- assertEquals(TEST_ADDRESS_PARCELED, mParcel.serverAddr);
- assertEquals(TEST_PREFIX_LENGTH, mParcel.serverAddrPrefixLength);
- }
-
- @Test
- public void testSetDefaultRouters() {
- mParcel.setDefaultRouters(TEST_ADDRESS_SET);
- assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.defaultRouters));
- }
-
- @Test
- public void testSetDnsServers() {
- mParcel.setDnsServers(TEST_ADDRESS_SET);
- assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.dnsServers));
- }
-
- @Test
- public void testSetExcludedAddrs() {
- mParcel.setExcludedAddrs(TEST_ADDRESS_SET);
- assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.excludedAddrs));
- }
-
- @Test
- public void testSetDhcpLeaseTimeSecs() {
- mParcel.setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS);
- assertEquals(TEST_LEASE_TIME_SECS, mParcel.dhcpLeaseTimeSecs);
- }
-
- @Test
- public void testSetLinkMtu() {
- mParcel.setLinkMtu(TEST_MTU);
- assertEquals(TEST_MTU, mParcel.linkMtu);
- }
-
- @Test
- public void testSetMetered() {
- mParcel.setMetered(true);
- assertTrue(mParcel.metered);
- mParcel.setMetered(false);
- assertFalse(mParcel.metered);
- }
-
- @Test
- public void testSetClientAddr() {
- mParcel.setSingleClientAddr(TEST_CLIENT_ADDRESS);
- assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.singleClientAddr);
- }
-
- private static Inet4Address inet4Addr(String addr) {
- return (Inet4Address) parseNumericAddress(addr);
- }
-
- private static Set<Integer> asSet(int[] ints) {
- return IntStream.of(ints).boxed().collect(Collectors.toSet());
- }
-}
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
deleted file mode 100644
index 2eb75895ac3e..000000000000
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ /dev/null
@@ -1,1201 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.INetd.IF_STATE_UP;
-import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.TetheringManager.TETHERING_BLUETOOTH;
-import static android.net.TetheringManager.TETHERING_NCM;
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.ip.IpServer.STATE_AVAILABLE;
-import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
-import static android.net.ip.IpServer.STATE_TETHERED;
-import static android.net.ip.IpServer.STATE_UNAVAILABLE;
-import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
-import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH;
-import static android.net.netlink.StructNdMsg.NUD_FAILED;
-import static android.net.netlink.StructNdMsg.NUD_REACHABLE;
-import static android.net.netlink.StructNdMsg.NUD_STALE;
-
-import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.usage.NetworkStatsManager;
-import android.net.INetd;
-import android.net.InetAddresses;
-import android.net.InterfaceConfigurationParcel;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.MacAddress;
-import android.net.RouteInfo;
-import android.net.TetherOffloadRuleParcel;
-import android.net.TetherStatsParcel;
-import android.net.dhcp.DhcpServerCallbacks;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpEventCallbacks;
-import android.net.dhcp.IDhcpServer;
-import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IpNeighborMonitor.NeighborEvent;
-import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
-import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.util.InterfaceParams;
-import android.net.util.InterfaceSet;
-import android.net.util.PrefixUtils;
-import android.net.util.SharedLog;
-import android.os.Build;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.test.TestLooper;
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.networkstack.tethering.BpfCoordinator;
-import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.PrivateAddressCoordinator;
-import com.android.networkstack.tethering.TetheringConfiguration;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class IpServerTest {
- @Rule
- public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
-
- private static final String IFACE_NAME = "testnet1";
- private static final String UPSTREAM_IFACE = "upstream0";
- private static final String UPSTREAM_IFACE2 = "upstream1";
- private static final int UPSTREAM_IFINDEX = 101;
- private static final int UPSTREAM_IFINDEX2 = 102;
- private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
- private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
- private static final int DHCP_LEASE_TIME_SECS = 3600;
- private static final boolean DEFAULT_USING_BPF_OFFLOAD = true;
-
- private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
- IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
- private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams(
- UPSTREAM_IFACE, UPSTREAM_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
- private static final InterfaceParams UPSTREAM_IFACE_PARAMS2 = new InterfaceParams(
- UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.ALL_ZEROS_ADDRESS,
- 1500 /* defaultMtu */);
-
- private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
-
- private final LinkAddress mTestAddress = new LinkAddress("192.168.42.5/24");
- private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
-
- @Mock private INetd mNetd;
- @Mock private IpServer.Callback mCallback;
- @Mock private SharedLog mSharedLog;
- @Mock private IDhcpServer mDhcpServer;
- @Mock private DadProxy mDadProxy;
- @Mock private RouterAdvertisementDaemon mRaDaemon;
- @Mock private IpNeighborMonitor mIpNeighborMonitor;
- @Mock private IpServer.Dependencies mDependencies;
- @Mock private PrivateAddressCoordinator mAddressCoordinator;
- @Mock private NetworkStatsManager mStatsManager;
- @Mock private TetheringConfiguration mTetherConfig;
-
- @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
-
- private final TestLooper mLooper = new TestLooper();
- private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
- ArgumentCaptor.forClass(LinkProperties.class);
- private IpServer mIpServer;
- private InterfaceConfigurationParcel mInterfaceConfiguration;
- private NeighborEventConsumer mNeighborEventConsumer;
- private BpfCoordinator mBpfCoordinator;
-
- private void initStateMachine(int interfaceType) throws Exception {
- initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
- }
-
- private void initStateMachine(int interfaceType, boolean usingLegacyDhcp,
- boolean usingBpfOffload) throws Exception {
- when(mDependencies.getDadProxy(any(), any())).thenReturn(mDadProxy);
- when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
- when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
- when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS);
- when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2);
-
- when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX);
- when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2);
-
- mInterfaceConfiguration = new InterfaceConfigurationParcel();
- mInterfaceConfiguration.flags = new String[0];
- if (interfaceType == TETHERING_BLUETOOTH) {
- mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR;
- mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
- }
-
- ArgumentCaptor<NeighborEventConsumer> neighborCaptor =
- ArgumentCaptor.forClass(NeighborEventConsumer.class);
- doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(),
- neighborCaptor.capture());
-
- mIpServer = new IpServer(
- IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator,
- mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies);
- mIpServer.start();
- mNeighborEventConsumer = neighborCaptor.getValue();
-
- // Starting the state machine always puts us in a consistent state and notifies
- // the rest of the world that we've changed from an unknown to available state.
- mLooper.dispatchAll();
- reset(mNetd, mCallback);
-
- when(mRaDaemon.start()).thenReturn(true);
- }
-
- private void initTetheredStateMachine(int interfaceType, String upstreamIface)
- throws Exception {
- initTetheredStateMachine(interfaceType, upstreamIface, false,
- DEFAULT_USING_BPF_OFFLOAD);
- }
-
- private void initTetheredStateMachine(int interfaceType, String upstreamIface,
- boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception {
- initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload);
- dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
- if (upstreamIface != null) {
- LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(upstreamIface);
- dispatchTetherConnectionChanged(upstreamIface, lp, 0);
- }
- reset(mNetd, mCallback, mAddressCoordinator);
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- mTestAddress);
- }
-
- private void setUpDhcpServer() throws Exception {
- doAnswer(inv -> {
- final IDhcpServerCallbacks cb = inv.getArgument(2);
- new Thread(() -> {
- try {
- cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
- }).run();
- return null;
- }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
- }
-
- @Before public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- mTestAddress);
- when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);
-
- mBpfCoordinator = spy(new BpfCoordinator(
- new BpfCoordinator.Dependencies() {
- @NonNull
- public Handler getHandler() {
- return new Handler(mLooper.getLooper());
- }
-
- @NonNull
- public INetd getNetd() {
- return mNetd;
- }
-
- @NonNull
- public NetworkStatsManager getNetworkStatsManager() {
- return mStatsManager;
- }
-
- @NonNull
- public SharedLog getSharedLog() {
- return mSharedLog;
- }
-
- @Nullable
- public TetheringConfiguration getTetherConfig() {
- return mTetherConfig;
- }
- }));
-
- setUpDhcpServer();
- }
-
- @Test
- public void startsOutAvailable() {
- when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
- .thenReturn(mIpNeighborMonitor);
- mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
- mNetd, mBpfCoordinator, mCallback, false /* usingLegacyDhcp */,
- DEFAULT_USING_BPF_OFFLOAD, mAddressCoordinator, mDependencies);
- mIpServer.start();
- mLooper.dispatchAll();
- verify(mCallback).updateInterfaceState(
- mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mCallback, mNetd);
- }
-
- @Test
- public void shouldDoNothingUntilRequested() throws Exception {
- initStateMachine(TETHERING_BLUETOOTH);
- final int [] noOp_commands = {
- IpServer.CMD_TETHER_UNREQUESTED,
- IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
- IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
- IpServer.CMD_START_TETHERING_ERROR,
- IpServer.CMD_STOP_TETHERING_ERROR,
- IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
- IpServer.CMD_TETHER_CONNECTION_CHANGED
- };
- for (int command : noOp_commands) {
- // None of these commands should trigger us to request action from
- // the rest of the system.
- dispatchCommand(command);
- verifyNoMoreInteractions(mNetd, mCallback);
- }
- }
-
- @Test
- public void handlesImmediateInterfaceDown() throws Exception {
- initStateMachine(TETHERING_BLUETOOTH);
-
- dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
- verify(mCallback).updateInterfaceState(
- mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
- verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mCallback);
- }
-
- @Test
- public void canBeTethered() throws Exception {
- initStateMachine(TETHERING_BLUETOOTH);
-
- dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder inOrder = inOrder(mCallback, mNetd);
- inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
- inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
- // One for ipv4 route, one for ipv6 link local route.
- inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
- any(), any());
- inOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mCallback).updateLinkProperties(
- eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mCallback);
- }
-
- @Test
- public void canUnrequestTethering() throws Exception {
- initTetheredStateMachine(TETHERING_BLUETOOTH, null);
-
- dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
- inOrder.verify(mNetd).tetherApplyDnsInterfaces();
- inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
- inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
- inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
- inOrder.verify(mAddressCoordinator).releaseDownstream(any());
- inOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mCallback).updateLinkProperties(
- eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
- }
-
- @Test
- public void canBeTetheredAsUsb() throws Exception {
- initStateMachine(TETHERING_USB);
-
- dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
- inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
- IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
- inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
- inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
- inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
- any(), any());
- inOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mCallback).updateLinkProperties(
- eq(mIpServer), mLinkPropertiesCaptor.capture());
- assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
- }
-
- @Test
- public void canBeTetheredAsWifiP2p() throws Exception {
- initStateMachine(TETHERING_WIFI_P2P);
-
- dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
- InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
- inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
- IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
- inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
- inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
- inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
- any(), any());
- inOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mCallback).updateLinkProperties(
- eq(mIpServer), mLinkPropertiesCaptor.capture());
- assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
- }
-
- @Test
- public void handlesFirstUpstreamChange() throws Exception {
- initTetheredStateMachine(TETHERING_BLUETOOTH, null);
-
- // Telling the state machine about its upstream interface triggers
- // a little more configuration.
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- InOrder inOrder = inOrder(mNetd);
- inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mCallback);
- }
-
- @Test
- public void handlesChangingUpstream() throws Exception {
- initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
-
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- verifyNoMoreInteractions(mNetd, mCallback);
- }
-
- @Test
- public void handlesChangingUpstreamNatFailure() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
-
- doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
-
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
- }
-
- @Test
- public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
-
- doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward(
- IFACE_NAME, UPSTREAM_IFACE2);
-
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
- }
-
- @Test
- public void canUnrequestTetheringWithUpstream() throws Exception {
- initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
-
- dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
- inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
- inOrder.verify(mNetd).tetherApplyDnsInterfaces();
- inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
- inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
- inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
- inOrder.verify(mAddressCoordinator).releaseDownstream(any());
- inOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mCallback).updateLinkProperties(
- eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
- }
-
- @Test
- public void interfaceDownLeadsToUnavailable() throws Exception {
- for (boolean shouldThrow : new boolean[]{true, false}) {
- initTetheredStateMachine(TETHERING_USB, null);
-
- if (shouldThrow) {
- doThrow(RemoteException.class).when(mNetd).tetherInterfaceRemove(IFACE_NAME);
- }
- dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
- InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
- // Currently IpServer interfaceSetCfg twice to stop IPv4. One just set interface down
- // Another one is set IPv4 to 0.0.0.0/0 as clearng ipv4 address.
- usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
- argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
- usbTeardownOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
- usbTeardownOrder.verify(mCallback).updateLinkProperties(
- eq(mIpServer), mLinkPropertiesCaptor.capture());
- assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
- }
- }
-
- @Test
- public void usbShouldBeTornDownOnTetherError() throws Exception {
- initStateMachine(TETHERING_USB);
-
- doThrow(RemoteException.class).when(mNetd).tetherInterfaceAdd(IFACE_NAME);
- dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
- usbTeardownOrder.verify(mNetd).interfaceSetCfg(
- argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
- usbTeardownOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
-
- usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
- argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
- usbTeardownOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
- usbTeardownOrder.verify(mCallback).updateLinkProperties(
- eq(mIpServer), mLinkPropertiesCaptor.capture());
- assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
- }
-
- @Test
- public void shouldTearDownUsbOnUpstreamError() throws Exception {
- initTetheredStateMachine(TETHERING_USB, null);
-
- doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString());
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
- usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
-
- usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
- argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
- usbTeardownOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_FORWARDING_ERROR);
- usbTeardownOrder.verify(mCallback).updateLinkProperties(
- eq(mIpServer), mLinkPropertiesCaptor.capture());
- assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
- }
-
- @Test
- public void ignoresDuplicateUpstreamNotifications() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
-
- verifyNoMoreInteractions(mNetd, mCallback);
-
- for (int i = 0; i < 5; i++) {
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mCallback);
- }
- }
-
- @Test
- public void startsDhcpServer() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-
- assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
- }
-
- @Test
- public void startsDhcpServerOnBluetooth() throws Exception {
- initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-
- assertDhcpStarted(mBluetoothPrefix);
- }
-
- @Test
- public void startsDhcpServerOnWifiP2p() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-
- assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
- }
-
- @Test
- public void startsDhcpServerOnNcm() throws Exception {
- initStateMachine(TETHERING_NCM);
- dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-
- assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
- }
-
- @Test
- public void testOnNewPrefixRequest() throws Exception {
- initStateMachine(TETHERING_NCM);
- dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
-
- final IDhcpEventCallbacks eventCallbacks;
- final ArgumentCaptor<IDhcpEventCallbacks> dhcpEventCbsCaptor =
- ArgumentCaptor.forClass(IDhcpEventCallbacks.class);
- verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), dhcpEventCbsCaptor.capture());
- eventCallbacks = dhcpEventCbsCaptor.getValue();
- assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
-
- final ArgumentCaptor<LinkProperties> lpCaptor =
- ArgumentCaptor.forClass(LinkProperties.class);
- InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
- inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
- // One for ipv4 route, one for ipv6 link local route.
- inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
- any(), any());
- inOrder.verify(mCallback).updateInterfaceState(
- mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
- inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
- verifyNoMoreInteractions(mCallback, mAddressCoordinator);
-
- // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
- // onNewPrefixRequest callback.
- final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- newAddress);
- eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
- mLooper.dispatchAll();
-
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(false));
- inOrder.verify(mNetd).tetherApplyDnsInterfaces();
- inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
- verifyNoMoreInteractions(mCallback);
-
- final LinkProperties linkProperties = lpCaptor.getValue();
- final List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses();
- assertEquals(1, linkProperties.getLinkAddresses().size());
- assertEquals(1, linkProperties.getRoutes().size());
- final IpPrefix prefix = new IpPrefix(linkAddresses.get(0).getAddress(),
- linkAddresses.get(0).getPrefixLength());
- assertNotEquals(prefix, new IpPrefix("192.168.42.0/24"));
-
- verify(mDhcpServer).updateParams(mDhcpParamsCaptor.capture(), any());
- assertDhcpServingParams(mDhcpParamsCaptor.getValue(), prefix);
- }
-
- @Test
- public void doesNotStartDhcpServerIfDisabled() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */,
- DEFAULT_USING_BPF_OFFLOAD);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-
- verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
- }
-
- private InetAddress addr(String addr) throws Exception {
- return InetAddresses.parseNumericAddress(addr);
- }
-
- private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
- mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr,
- nudState, mac));
- mLooper.dispatchAll();
- }
-
- private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
- mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr,
- nudState, mac));
- mLooper.dispatchAll();
- }
-
- /**
- * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable
- * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as:
- *
- * private void checkFooCalled(StableParcelable p, ...) {
- * ArgumentCaptor<FooParam> captor = ArgumentCaptor.forClass(FooParam.class);
- * verify(mMock).foo(captor.capture());
- * Foo foo = captor.getValue();
- * assertFooMatchesExpectations(foo);
- * }
- *
- * almost works, but not quite. This is because if the code under test calls foo() twice, the
- * first call to checkFooCalled() matches both the calls, putting both calls into the captor,
- * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito
- * features such as never(), inOrder(), etc.
- *
- * This approach isn't great because if the match fails, the error message is unhelpful
- * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does
- * work.
- *
- * TODO: consider making the error message more readable by adding a method that catching the
- * AssertionFailedError and throwing a new assertion with more details. See
- * NetworkMonitorTest#verifyNetworkTested.
- *
- * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the
- * TooManyActualInvocations problem described above by forcing the caller of the custom assert
- * method to specify all expected invocations in one call. This is useful when the stable
- * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and
- * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here
- * because there is no such object.
- */
- private static class TetherOffloadRuleParcelMatcher implements
- ArgumentMatcher<TetherOffloadRuleParcel> {
- public final int upstreamIfindex;
- public final InetAddress dst;
- public final MacAddress dstMac;
-
- TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
- this.upstreamIfindex = upstreamIfindex;
- this.dst = dst;
- this.dstMac = dstMac;
- }
-
- public boolean matches(TetherOffloadRuleParcel parcel) {
- return upstreamIfindex == parcel.inputInterfaceIndex
- && (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex)
- && Arrays.equals(dst.getAddress(), parcel.destination)
- && (128 == parcel.prefixLength)
- && Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address)
- && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address);
- }
-
- public String toString() {
- return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s",
- upstreamIfindex, dst.getHostAddress(), dstMac);
- }
- }
-
- @NonNull
- private static TetherOffloadRuleParcel matches(
- int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
- return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac));
- }
-
- @NonNull
- private static Ipv6ForwardingRule makeForwardingRule(
- int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) {
- return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index,
- (Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac);
- }
-
- @NonNull
- private static TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) {
- TetherStatsParcel parcel = new TetherStatsParcel();
- parcel.ifIndex = ifIndex;
- return parcel;
- }
-
- private void resetNetdAndBpfCoordinator() throws Exception {
- reset(mNetd, mBpfCoordinator);
- when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
- when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX))
- .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX));
- when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX2))
- .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX2));
- }
-
- @Test
- public void addRemoveipv6ForwardingRules() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
- DEFAULT_USING_BPF_OFFLOAD);
-
- final int myIfindex = TEST_IFACE_PARAMS.index;
- final int notMyIfindex = myIfindex - 1;
-
- final MacAddress myMac = TEST_IFACE_PARAMS.macAddr;
- final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1");
- final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2");
- final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1");
- final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234");
- final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
- final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
- final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
-
- resetNetdAndBpfCoordinator();
- verifyNoMoreInteractions(mBpfCoordinator, mNetd);
-
- // TODO: Perhaps verify the interaction of tetherOffloadSetInterfaceQuota and
- // tetherOffloadGetAndClearStats in netd while the rules are changed.
-
- // Events on other interfaces are ignored.
- recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd);
-
- // Events on this interface are received and sent to netd.
- recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- verify(mBpfCoordinator).tetherOffloadRuleAdd(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
- verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
- resetNetdAndBpfCoordinator();
-
- recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- verify(mBpfCoordinator).tetherOffloadRuleAdd(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
- verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
- resetNetdAndBpfCoordinator();
-
- // Link-local and multicast neighbors are ignored.
- recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd);
- recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd);
-
- // A neighbor that is no longer valid causes the rule to be removed.
- // NUD_FAILED events do not have a MAC address.
- recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
- verify(mBpfCoordinator).tetherOffloadRuleRemove(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull));
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macNull));
- resetNetdAndBpfCoordinator();
-
- // A neighbor that is deleted causes the rule to be removed.
- recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
- verify(mBpfCoordinator).tetherOffloadRuleRemove(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull));
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macNull));
- resetNetdAndBpfCoordinator();
-
- // Upstream changes result in updating the rules.
- recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- resetNetdAndBpfCoordinator();
-
- InOrder inOrder = inOrder(mNetd);
- LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(UPSTREAM_IFACE2);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
- verify(mBpfCoordinator).tetherOffloadRuleUpdate(mIpServer, UPSTREAM_IFINDEX2);
- inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
- inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
- resetNetdAndBpfCoordinator();
-
- // When the upstream is lost, rules are removed.
- dispatchTetherConnectionChanged(null, null, 0);
- // Clear function is called two times by:
- // - processMessage CMD_TETHER_CONNECTION_CHANGED for the upstream is lost.
- // - processMessage CMD_IPV6_TETHER_UPDATE for the IPv6 upstream is lost.
- // See dispatchTetherConnectionChanged.
- verify(mBpfCoordinator, times(2)).tetherOffloadRuleClear(mIpServer);
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA));
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
- resetNetdAndBpfCoordinator();
-
- // If the upstream is IPv4-only, no rules are added.
- dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- resetNetdAndBpfCoordinator();
- recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- // Clear function is called by #updateIpv6ForwardingRules for the IPv6 upstream is lost.
- verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
- verifyNoMoreInteractions(mBpfCoordinator, mNetd);
-
- // Rules can be added again once upstream IPv6 connectivity is available.
- lp.setInterfaceName(UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
- recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- verify(mBpfCoordinator).tetherOffloadRuleAdd(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
- verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
- verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
- verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
-
- // If upstream IPv6 connectivity is lost, rules are removed.
- resetNetdAndBpfCoordinator();
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
- verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
-
- // When the interface goes down, rules are removed.
- lp.setInterfaceName(UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
- recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- verify(mBpfCoordinator).tetherOffloadRuleAdd(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
- verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
- verify(mBpfCoordinator).tetherOffloadRuleAdd(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
- verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
- resetNetdAndBpfCoordinator();
-
- mIpServer.stop();
- mLooper.dispatchAll();
- verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
- verify(mIpNeighborMonitor).stop();
- resetNetdAndBpfCoordinator();
- }
-
- @Test
- public void enableDisableUsingBpfOffload() throws Exception {
- final int myIfindex = TEST_IFACE_PARAMS.index;
- final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
- final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
- final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
-
- // Expect that rules can be only added/removed when the BPF offload config is enabled.
- // Note that the BPF offload disabled case is not a realistic test case. Because IP
- // neighbor monitor doesn't start if BPF offload is disabled, there should have no
- // neighbor event listening. This is used for testing the protection check just in case.
- // TODO: Perhaps remove the BPF offload disabled case test once this check isn't needed
- // anymore.
-
- // [1] Enable BPF offload.
- // A neighbor that is added or deleted causes the rule to be added or removed.
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
- true /* usingBpfOffload */);
- resetNetdAndBpfCoordinator();
-
- recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
- verify(mBpfCoordinator).tetherOffloadRuleAdd(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macA));
- verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA));
- resetNetdAndBpfCoordinator();
-
- recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
- verify(mBpfCoordinator).tetherOffloadRuleRemove(
- mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull));
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull));
- resetNetdAndBpfCoordinator();
-
- // [2] Disable BPF offload.
- // A neighbor that is added or deleted doesn’t cause the rule to be added or removed.
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
- false /* usingBpfOffload */);
- resetNetdAndBpfCoordinator();
-
- recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
- verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(any(), any());
- verify(mNetd, never()).tetherOffloadRuleAdd(any());
- resetNetdAndBpfCoordinator();
-
- recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
- verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any());
- verify(mNetd, never()).tetherOffloadRuleRemove(any());
- resetNetdAndBpfCoordinator();
- }
-
- @Test
- public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
- false /* usingBpfOffload */);
-
- // IP neighbor monitor doesn't start if BPF offload is disabled.
- verify(mIpNeighborMonitor, never()).start();
- }
-
- private LinkProperties buildIpv6OnlyLinkProperties(final String iface) {
- final LinkProperties linkProp = new LinkProperties();
- linkProp.setInterfaceName(iface);
- linkProp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
- linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, iface, RTN_UNICAST));
- final InetAddress dns = InetAddresses.parseNumericAddress("2001:4860:4860::8888");
- linkProp.addDnsServer(dns);
-
- return linkProp;
- }
-
- @Test
- public void testAdjustTtlValue() throws Exception {
- final ArgumentCaptor<RaParams> raParamsCaptor =
- ArgumentCaptor.forClass(RaParams.class);
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
- final RaParams noV6Params = raParamsCaptor.getValue();
- assertEquals(65, noV6Params.hopLimit);
- reset(mRaDaemon);
-
- when(mNetd.getProcSysNet(
- INetd.IPV6, INetd.CONF, UPSTREAM_IFACE, "hop_limit")).thenReturn("64");
- final LinkProperties lp = buildIpv6OnlyLinkProperties(UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 1);
- verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
- final RaParams nonCellularParams = raParamsCaptor.getValue();
- assertEquals(65, nonCellularParams.hopLimit);
- reset(mRaDaemon);
-
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
- verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
- final RaParams noUpstream = raParamsCaptor.getValue();
- assertEquals(65, nonCellularParams.hopLimit);
- reset(mRaDaemon);
-
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
- verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
- final RaParams cellularParams = raParamsCaptor.getValue();
- assertEquals(63, cellularParams.hopLimit);
- reset(mRaDaemon);
- }
-
- @Test
- public void testStopObsoleteDhcpServer() throws Exception {
- final ArgumentCaptor<DhcpServerCallbacks> cbCaptor =
- ArgumentCaptor.forClass(DhcpServerCallbacks.class);
- doNothing().when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(),
- cbCaptor.capture());
- initStateMachine(TETHERING_WIFI);
- dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
- verify(mDhcpServer, never()).startWithCallbacks(any(), any());
-
- // No stop dhcp server because dhcp server is not created yet.
- dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- verify(mDhcpServer, never()).stop(any());
-
- // Stop obsolete dhcp server.
- try {
- final DhcpServerCallbacks cb = cbCaptor.getValue();
- cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
- mLooper.dispatchAll();
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
- verify(mDhcpServer).stop(any());
- }
-
- private void assertDhcpServingParams(final DhcpServingParamsParcel params,
- final IpPrefix prefix) {
- // Last address byte is random
- assertTrue(prefix.contains(intToInet4AddressHTH(params.serverAddr)));
- assertEquals(prefix.getPrefixLength(), params.serverAddrPrefixLength);
- assertEquals(1, params.defaultRouters.length);
- assertEquals(params.serverAddr, params.defaultRouters[0]);
- assertEquals(1, params.dnsServers.length);
- assertEquals(params.serverAddr, params.dnsServers[0]);
- assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
- if (mIpServer.interfaceType() == TETHERING_NCM) {
- assertTrue(params.changePrefixOnDecline);
- }
- }
-
- private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
- verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
- verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- assertDhcpServingParams(mDhcpParamsCaptor.getValue(), expectedPrefix);
- }
-
- /**
- * Send a command to the state machine under test, and run the event loop to idle.
- *
- * @param command One of the IpServer.CMD_* constants.
- * @param arg1 An additional argument to pass.
- */
- private void dispatchCommand(int command, int arg1) {
- mIpServer.sendMessage(command, arg1);
- mLooper.dispatchAll();
- }
-
- /**
- * Send a command to the state machine under test, and run the event loop to idle.
- *
- * @param command One of the IpServer.CMD_* constants.
- */
- private void dispatchCommand(int command) {
- mIpServer.sendMessage(command);
- mLooper.dispatchAll();
- }
-
- /**
- * Special override to tell the state machine that the upstream interface has changed.
- *
- * @see #dispatchCommand(int)
- * @param upstreamIface String name of upstream interface (or null)
- * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream.
- */
- private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp,
- int ttlAdjustment) {
- dispatchTetherConnectionChanged(upstreamIface);
- mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp);
- mLooper.dispatchAll();
- }
-
- private void dispatchTetherConnectionChanged(String upstreamIface) {
- final InterfaceSet ifs = (upstreamIface != null) ? new InterfaceSet(upstreamIface) : null;
- mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifs);
- mLooper.dispatchAll();
- }
-
- private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
- // Find the first IPv4 LinkAddress.
- LinkAddress addr4 = null;
- for (LinkAddress addr : lp.getLinkAddresses()) {
- if (!(addr.getAddress() instanceof Inet4Address)) continue;
- addr4 = addr;
- break;
- }
- assertNotNull("missing IPv4 address", addr4);
-
- final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength());
- // Assert the presence of the associated directly connected route.
- final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(),
- RouteInfo.RTN_UNICAST);
- assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
- lp.getRoutes().contains(directlyConnected));
- }
-
- private void assertNoAddressesNorRoutes(LinkProperties lp) {
- assertTrue(lp.getLinkAddresses().isEmpty());
- assertTrue(lp.getRoutes().isEmpty());
- // We also check that interface name is non-empty, because we should
- // never see an empty interface name in any LinkProperties update.
- assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
- }
-
- private boolean assertContainsFlag(String[] flags, String match) {
- for (String flag : flags) {
- if (flag.equals(match)) return true;
- }
- fail("Missing flag: " + match);
- return false;
- }
-
- private boolean assertNotContainsFlag(String[] flags, String match) {
- for (String flag : flags) {
- if (flag.equals(match)) {
- fail("Unexpected flag: " + match);
- return false;
- }
- }
- return true;
- }
-
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
- public void dadProxyUpdates() throws Exception {
- InOrder inOrder = inOrder(mDadProxy);
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
-
- // Add an upstream without IPv6.
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
- inOrder.verify(mDadProxy).setUpstreamIface(null);
-
- // Add IPv6 to the upstream.
- LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(UPSTREAM_IFACE);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
- inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
-
- // Change upstream.
- // New linkproperties is needed, otherwise changing the iface has no impact.
- LinkProperties lp2 = new LinkProperties();
- lp2.setInterfaceName(UPSTREAM_IFACE2);
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0);
- inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS2);
-
- // Lose IPv6 on the upstream...
- dispatchTetherConnectionChanged(UPSTREAM_IFACE2, null, 0);
- inOrder.verify(mDadProxy).setUpstreamIface(null);
-
- // ... and regain it on a different upstream.
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
- inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
-
- // Lose upstream.
- dispatchTetherConnectionChanged(null, null, 0);
- inOrder.verify(mDadProxy).setUpstreamIface(null);
-
- // Regain upstream.
- dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
- inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
-
- // Stop tethering.
- mIpServer.stop();
- mLooper.dispatchAll();
- }
-
- private void checkDadProxyEnabled(boolean expectEnabled) throws Exception {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- InOrder inOrder = inOrder(mDadProxy);
- // Add IPv6 to the upstream.
- LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(UPSTREAM_IFACE);
- if (expectEnabled) {
- inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
- } else {
- inOrder.verifyNoMoreInteractions();
- }
- // Stop tethering.
- mIpServer.stop();
- mLooper.dispatchAll();
- if (expectEnabled) {
- inOrder.verify(mDadProxy).stop();
- }
- else {
- verify(mDependencies, never()).getDadProxy(any(), any());
- }
- }
- @Test @IgnoreAfter(Build.VERSION_CODES.R)
- public void testDadProxyUpdates_DisabledUpToR() throws Exception {
- checkDadProxyEnabled(false);
- }
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
- public void testDadProxyUpdates_EnabledAfterR() throws Exception {
- checkDadProxyEnabled(true);
- }
-}
diff --git a/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java b/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
deleted file mode 100644
index ea084b607868..000000000000
--- a/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class InterfaceSetTest {
- @Test
- public void testNullNamesIgnored() {
- final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null);
- assertEquals(2, set.ifnames.size());
- assertTrue(set.ifnames.contains("if1"));
- assertTrue(set.ifnames.contains("if2"));
- }
-
- @Test
- public void testToString() {
- final InterfaceSet set = new InterfaceSet("if1", "if2");
- final String setString = set.toString();
- assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]"));
- }
-
- @Test
- public void testToString_Empty() {
- final InterfaceSet set = new InterfaceSet(null, null);
- assertEquals("[]", set.toString());
- }
-
- @Test
- public void testEquals() {
- assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1"));
- assertEquals(new InterfaceSet(null, null), new InterfaceSet());
- assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2")));
- assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1")));
- assertFalse(new InterfaceSet().equals(null));
- }
-}
diff --git a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
deleted file mode 100644
index 91c7771cc7fe..000000000000
--- a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
+++ /dev/null
@@ -1,87 +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.net.util;
-
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import android.net.LinkAddress;
-import android.net.TetheringRequestParcel;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.testutils.MiscAsserts;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TetheringUtilsTest {
- private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24");
- private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24");
- private TetheringRequestParcel mTetheringRequest;
-
- @Before
- public void setUp() {
- mTetheringRequest = makeTetheringRequestParcel();
- }
-
- public TetheringRequestParcel makeTetheringRequestParcel() {
- final TetheringRequestParcel request = new TetheringRequestParcel();
- request.tetheringType = TETHERING_WIFI;
- request.localIPv4Address = TEST_SERVER_ADDR;
- request.staticClientAddress = TEST_CLIENT_ADDR;
- request.exemptFromEntitlementCheck = false;
- request.showProvisioningUi = true;
- return request;
- }
-
- @Test
- public void testIsTetheringRequestEquals() throws Exception {
- TetheringRequestParcel request = makeTetheringRequestParcel();
-
- assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest));
- assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
- assertTrue(TetheringUtils.isTetheringRequestEquals(null, null));
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null));
- assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest));
-
- request = makeTetheringRequestParcel();
- request.tetheringType = TETHERING_USB;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- request = makeTetheringRequestParcel();
- request.localIPv4Address = null;
- request.staticClientAddress = null;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- request = makeTetheringRequestParcel();
- request.exemptFromEntitlementCheck = true;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- request = makeTetheringRequestParcel();
- request.showProvisioningUi = false;
- assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
-
- MiscAsserts.assertFieldCountEquals(5, TetheringRequestParcel.class);
- }
-}
diff --git a/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java b/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
deleted file mode 100644
index 5a9b6e380ea9..000000000000
--- a/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.reset;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.test.BroadcastInterceptingContext;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class VersionedBroadcastListenerTest {
- private static final String TAG = VersionedBroadcastListenerTest.class.getSimpleName();
- private static final String ACTION_TEST = "action.test.happy.broadcasts";
-
- @Mock private Context mContext;
- private BroadcastInterceptingContext mServiceContext;
- private Handler mHandler;
- private VersionedBroadcastListener mListener;
- private int mCallbackCount;
-
- private void doCallback() {
- mCallbackCount++;
- }
-
- private class MockContext extends BroadcastInterceptingContext {
- MockContext(Context base) {
- super(base);
- }
- }
-
- @BeforeClass
- public static void setUpBeforeClass() throws Exception {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
- @Before public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- reset(mContext);
- mServiceContext = new MockContext(mContext);
- mHandler = new Handler(Looper.myLooper());
- mCallbackCount = 0;
- final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_TEST);
- mListener = new VersionedBroadcastListener(
- TAG, mServiceContext, mHandler, filter, (Intent intent) -> doCallback());
- }
-
- @After public void tearDown() throws Exception {
- if (mListener != null) {
- mListener.stopListening();
- mListener = null;
- }
- }
-
- private void sendBroadcast() {
- final Intent intent = new Intent(ACTION_TEST);
- mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- @Test
- public void testBasicListening() {
- assertEquals(0, mCallbackCount);
- mListener.startListening();
- for (int i = 0; i < 5; i++) {
- sendBroadcast();
- assertEquals(i + 1, mCallbackCount);
- }
- mListener.stopListening();
- }
-
- @Test
- public void testBroadcastsBeforeStartAreIgnored() {
- assertEquals(0, mCallbackCount);
- for (int i = 0; i < 5; i++) {
- sendBroadcast();
- assertEquals(0, mCallbackCount);
- }
-
- mListener.startListening();
- sendBroadcast();
- assertEquals(1, mCallbackCount);
- }
-
- @Test
- public void testBroadcastsAfterStopAreIgnored() {
- mListener.startListening();
- sendBroadcast();
- assertEquals(1, mCallbackCount);
- mListener.stopListening();
-
- for (int i = 0; i < 5; i++) {
- sendBroadcast();
- assertEquals(1, mCallbackCount);
- }
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
deleted file mode 100644
index 64242ae8255f..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ /dev/null
@@ -1,607 +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.networkstack.tethering;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStats.UID_TETHERING;
-import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
-
-import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
-import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
-import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
-import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.argThat;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.usage.NetworkStatsManager;
-import android.net.INetd;
-import android.net.InetAddresses;
-import android.net.MacAddress;
-import android.net.NetworkStats;
-import android.net.TetherOffloadRuleParcel;
-import android.net.TetherStatsParcel;
-import android.net.ip.IpServer;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.test.TestLooper;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.testutils.TestableNetworkStatsProviderCbBinder;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class BpfCoordinatorTest {
- private static final int DOWNSTREAM_IFINDEX = 10;
- private static final MacAddress DOWNSTREAM_MAC = MacAddress.ALL_ZEROS_ADDRESS;
- private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1");
- private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2");
- private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a");
- private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b");
-
- @Mock private NetworkStatsManager mStatsManager;
- @Mock private INetd mNetd;
- @Mock private IpServer mIpServer;
- @Mock private TetheringConfiguration mTetherConfig;
-
- // Late init since methods must be called by the thread that created this object.
- private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
- private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider;
- private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
- ArgumentCaptor.forClass(ArrayList.class);
- private final TestLooper mTestLooper = new TestLooper();
- private BpfCoordinator.Dependencies mDeps =
- new BpfCoordinator.Dependencies() {
- @NonNull
- public Handler getHandler() {
- return new Handler(mTestLooper.getLooper());
- }
-
- @NonNull
- public INetd getNetd() {
- return mNetd;
- }
-
- @NonNull
- public NetworkStatsManager getNetworkStatsManager() {
- return mStatsManager;
- }
-
- @NonNull
- public SharedLog getSharedLog() {
- return new SharedLog("test");
- }
-
- @Nullable
- public TetheringConfiguration getTetherConfig() {
- return mTetherConfig;
- }
- };
-
- @Before public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);
- }
-
- private void waitForIdle() {
- mTestLooper.dispatchAll();
- }
-
- private void setupFunctioningNetdInterface() throws Exception {
- when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
- }
-
- @NonNull
- private BpfCoordinator makeBpfCoordinator() throws Exception {
- final BpfCoordinator coordinator = new BpfCoordinator(mDeps);
- final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider>
- tetherStatsProviderCaptor =
- ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class);
- verify(mStatsManager).registerNetworkStatsProvider(anyString(),
- tetherStatsProviderCaptor.capture());
- mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
- assertNotNull(mTetherStatsProvider);
- mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
- mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
- return coordinator;
- }
-
- @NonNull
- private static NetworkStats.Entry buildTestEntry(@NonNull StatsType how,
- @NonNull String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- return new NetworkStats.Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING,
- SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes,
- rxPackets, txBytes, txPackets, 0L);
- }
-
- @NonNull
- private static TetherStatsParcel buildTestTetherStatsParcel(@NonNull Integer ifIndex,
- long rxBytes, long rxPackets, long txBytes, long txPackets) {
- final TetherStatsParcel parcel = new TetherStatsParcel();
- parcel.ifIndex = ifIndex;
- parcel.rxBytes = rxBytes;
- parcel.rxPackets = rxPackets;
- parcel.txBytes = txBytes;
- parcel.txPackets = txPackets;
- return parcel;
- }
-
- // Set up specific tether stats list and wait for the stats cache is updated by polling thread
- // in the coordinator. Beware of that it is only used for the default polling interval.
- private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception {
- when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList);
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- }
-
- @Test
- public void testGetForwardedStats() throws Exception {
- setupFunctioningNetdInterface();
-
- final BpfCoordinator coordinator = makeBpfCoordinator();
- coordinator.startPolling();
-
- final String wlanIface = "wlan0";
- final Integer wlanIfIndex = 100;
- final String mobileIface = "rmnet_data0";
- final Integer mobileIfIndex = 101;
-
- // Add interface name to lookup table. In realistic case, the upstream interface name will
- // be added by IpServer when IpServer has received with a new IPv6 upstream update event.
- coordinator.addUpstreamNameToLookupTable(wlanIfIndex, wlanIface);
- coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
-
- // [1] Both interface stats are changed.
- // Setup the tether stats of wlan and mobile interface. Note that move forward the time of
- // the looper to make sure the new tether stats has been updated by polling update thread.
- setTetherOffloadStatsList(new TetherStatsParcel[] {
- buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
- buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)});
-
- final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 1000, 100, 2000, 200))
- .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 3000, 300, 4000, 400));
-
- final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 1000, 100, 2000, 200))
- .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 3000, 300, 4000, 400));
-
- // Force pushing stats update to verify the stats reported.
- // TODO: Perhaps make #expectNotifyStatsUpdated to use test TetherStatsParcel object for
- // verifying the notification.
- mTetherStatsProvider.pushTetherStats();
- mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats);
-
- // [2] Only one interface stats is changed.
- // The tether stats of mobile interface is accumulated and The tether stats of wlan
- // interface is the same.
- setTetherOffloadStatsList(new TetherStatsParcel[] {
- buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
- buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)});
-
- final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 0, 0, 0, 0))
- .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 10, 20, 30, 40));
-
- final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 0, 0, 0, 0))
- .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 10, 20, 30, 40));
-
- // Force pushing stats update to verify that only diff of stats is reported.
- mTetherStatsProvider.pushTetherStats();
- mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff,
- expectedUidStatsDiff);
-
- // [3] Stop coordinator.
- // Shutdown the coordinator and clear the invocation history, especially the
- // tetherOffloadGetStats() calls.
- coordinator.stopPolling();
- clearInvocations(mNetd);
-
- // Verify the polling update thread stopped.
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- verify(mNetd, never()).tetherOffloadGetStats();
- }
-
- @Test
- public void testOnSetAlert() throws Exception {
- setupFunctioningNetdInterface();
-
- final BpfCoordinator coordinator = makeBpfCoordinator();
- coordinator.startPolling();
-
- final String mobileIface = "rmnet_data0";
- final Integer mobileIfIndex = 100;
- coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
-
- // Verify that set quota to 0 will immediately triggers a callback.
- mTetherStatsProvider.onSetAlert(0);
- waitForIdle();
- mTetherStatsProviderCb.expectNotifyAlertReached();
-
- // Verify that notifyAlertReached never fired if quota is not yet reached.
- when(mNetd.tetherOffloadGetStats()).thenReturn(
- new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)});
- mTetherStatsProvider.onSetAlert(100);
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- mTetherStatsProviderCb.assertNoCallback();
-
- // Verify that notifyAlertReached fired when quota is reached.
- when(mNetd.tetherOffloadGetStats()).thenReturn(
- new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)});
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- mTetherStatsProviderCb.expectNotifyAlertReached();
-
- // Verify that set quota with UNLIMITED won't trigger any callback.
- mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED);
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- mTetherStatsProviderCb.assertNoCallback();
- }
-
- // The custom ArgumentMatcher simply comes from IpServerTest.
- // TODO: move both of them into a common utility class for reusing the code.
- private static class TetherOffloadRuleParcelMatcher implements
- ArgumentMatcher<TetherOffloadRuleParcel> {
- public final int upstreamIfindex;
- public final int downstreamIfindex;
- public final Inet6Address address;
- public final MacAddress srcMac;
- public final MacAddress dstMac;
-
- TetherOffloadRuleParcelMatcher(@NonNull Ipv6ForwardingRule rule) {
- upstreamIfindex = rule.upstreamIfindex;
- downstreamIfindex = rule.downstreamIfindex;
- address = rule.address;
- srcMac = rule.srcMac;
- dstMac = rule.dstMac;
- }
-
- public boolean matches(@NonNull TetherOffloadRuleParcel parcel) {
- return upstreamIfindex == parcel.inputInterfaceIndex
- && (downstreamIfindex == parcel.outputInterfaceIndex)
- && Arrays.equals(address.getAddress(), parcel.destination)
- && (128 == parcel.prefixLength)
- && Arrays.equals(srcMac.toByteArray(), parcel.srcL2Address)
- && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address);
- }
-
- public String toString() {
- return String.format("TetherOffloadRuleParcelMatcher(%d, %d, %s, %s, %s",
- upstreamIfindex, downstreamIfindex, address.getHostAddress(), srcMac, dstMac);
- }
- }
-
- @NonNull
- private TetherOffloadRuleParcel matches(@NonNull Ipv6ForwardingRule rule) {
- return argThat(new TetherOffloadRuleParcelMatcher(rule));
- }
-
- @NonNull
- private static Ipv6ForwardingRule buildTestForwardingRule(
- int upstreamIfindex, @NonNull InetAddress address, @NonNull MacAddress dstMac) {
- return new Ipv6ForwardingRule(upstreamIfindex, DOWNSTREAM_IFINDEX, (Inet6Address) address,
- DOWNSTREAM_MAC, dstMac);
- }
-
- @Test
- public void testSetDataLimit() throws Exception {
- setupFunctioningNetdInterface();
-
- final BpfCoordinator coordinator = makeBpfCoordinator();
-
- final String mobileIface = "rmnet_data0";
- final Integer mobileIfIndex = 100;
- coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
-
- // [1] Default limit.
- // Set the unlimited quota as default if the service has never applied a data limit for a
- // given upstream. Note that the data limit only be applied on an upstream which has rules.
- final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A);
- final InOrder inOrder = inOrder(mNetd);
- coordinator.tetherOffloadRuleAdd(mIpServer, rule);
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(rule));
- inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED);
- inOrder.verifyNoMoreInteractions();
-
- // [2] Specific limit.
- // Applying the data limit boundary {min, 1gb, max, infinity} on current upstream.
- for (final long quota : new long[] {0, 1048576000, Long.MAX_VALUE, QUOTA_UNLIMITED}) {
- mTetherStatsProvider.onSetLimit(mobileIface, quota);
- waitForIdle();
- inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, quota);
- inOrder.verifyNoMoreInteractions();
- }
-
- // [3] Invalid limit.
- // The valid range of quota is 0..max_int64 or -1 (unlimited).
- final long invalidLimit = Long.MIN_VALUE;
- try {
- mTetherStatsProvider.onSetLimit(mobileIface, invalidLimit);
- waitForIdle();
- fail("No exception thrown for invalid limit " + invalidLimit + ".");
- } catch (IllegalArgumentException expected) {
- assertEquals(expected.getMessage(), "invalid quota value " + invalidLimit);
- }
- }
-
- // TODO: Test the case in which the rules are changed from different IpServer objects.
- @Test
- public void testSetDataLimitOnRuleChange() throws Exception {
- setupFunctioningNetdInterface();
-
- final BpfCoordinator coordinator = makeBpfCoordinator();
-
- final String mobileIface = "rmnet_data0";
- final Integer mobileIfIndex = 100;
- coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
-
- // Applying a data limit to the current upstream does not take any immediate action.
- // The data limit could be only set on an upstream which has rules.
- final long limit = 12345;
- final InOrder inOrder = inOrder(mNetd);
- mTetherStatsProvider.onSetLimit(mobileIface, limit);
- waitForIdle();
- inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
-
- // Adding the first rule on current upstream immediately sends the quota to netd.
- final Ipv6ForwardingRule ruleA = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A);
- coordinator.tetherOffloadRuleAdd(mIpServer, ruleA);
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleA));
- inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, limit);
- inOrder.verifyNoMoreInteractions();
-
- // Adding the second rule on current upstream does not send the quota to netd.
- final Ipv6ForwardingRule ruleB = buildTestForwardingRule(mobileIfIndex, NEIGH_B, MAC_B);
- coordinator.tetherOffloadRuleAdd(mIpServer, ruleB);
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ruleB));
- inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
-
- // Removing the second rule on current upstream does not send the quota to netd.
- coordinator.tetherOffloadRuleRemove(mIpServer, ruleB);
- inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleB));
- inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong());
-
- // Removing the last rule on current upstream immediately sends the cleanup stuff to netd.
- when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
- .thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0));
- coordinator.tetherOffloadRuleRemove(mIpServer, ruleA);
- inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ruleA));
- inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex);
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testTetherOffloadRuleUpdateAndClear() throws Exception {
- setupFunctioningNetdInterface();
-
- final BpfCoordinator coordinator = makeBpfCoordinator();
-
- final String ethIface = "eth1";
- final String mobileIface = "rmnet_data0";
- final Integer ethIfIndex = 100;
- final Integer mobileIfIndex = 101;
- coordinator.addUpstreamNameToLookupTable(ethIfIndex, ethIface);
- coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
-
- final InOrder inOrder = inOrder(mNetd);
-
- // Before the rule test, here are the additional actions while the rules are changed.
- // - After adding the first rule on a given upstream, the coordinator adds a data limit.
- // If the service has never applied the data limit, set an unlimited quota as default.
- // - After removing the last rule on a given upstream, the coordinator gets the last stats.
- // Then, it clears the stats and the limit entry from BPF maps.
- // See tetherOffloadRule{Add, Remove, Clear, Clean}.
-
- // [1] Adding rules on the upstream Ethernet.
- // Note that the default data limit is applied after the first rule is added.
- final Ipv6ForwardingRule ethernetRuleA = buildTestForwardingRule(
- ethIfIndex, NEIGH_A, MAC_A);
- final Ipv6ForwardingRule ethernetRuleB = buildTestForwardingRule(
- ethIfIndex, NEIGH_B, MAC_B);
-
- coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleA);
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleA));
- inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(ethIfIndex, QUOTA_UNLIMITED);
-
- coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB);
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(ethernetRuleB));
-
- // [2] Update the existing rules from Ethernet to cellular.
- final Ipv6ForwardingRule mobileRuleA = buildTestForwardingRule(
- mobileIfIndex, NEIGH_A, MAC_A);
- final Ipv6ForwardingRule mobileRuleB = buildTestForwardingRule(
- mobileIfIndex, NEIGH_B, MAC_B);
- when(mNetd.tetherOffloadGetAndClearStats(ethIfIndex))
- .thenReturn(buildTestTetherStatsParcel(ethIfIndex, 10, 20, 30, 40));
-
- // Update the existing rules for upstream changes. The rules are removed and re-added one
- // by one for updating upstream interface index by #tetherOffloadRuleUpdate.
- coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex);
- inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleA));
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleA));
- inOrder.verify(mNetd).tetherOffloadSetInterfaceQuota(mobileIfIndex, QUOTA_UNLIMITED);
- inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(ethernetRuleB));
- inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ethIfIndex);
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(mobileRuleB));
-
- // [3] Clear all rules for a given IpServer.
- when(mNetd.tetherOffloadGetAndClearStats(mobileIfIndex))
- .thenReturn(buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80));
- coordinator.tetherOffloadRuleClear(mIpServer);
- inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleA));
- inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(mobileRuleB));
- inOrder.verify(mNetd).tetherOffloadGetAndClearStats(mobileIfIndex);
-
- // [4] Force pushing stats update to verify that the last diff of stats is reported on all
- // upstreams.
- mTetherStatsProvider.pushTetherStats();
- mTetherStatsProviderCb.expectNotifyStatsUpdated(
- new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_IFACE, ethIface, 10, 20, 30, 40))
- .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 50, 60, 70, 80)),
- new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_UID, ethIface, 10, 20, 30, 40))
- .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 50, 60, 70, 80)));
- }
-
- @Test
- public void testTetheringConfigDisable() throws Exception {
- setupFunctioningNetdInterface();
- when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(false);
-
- final BpfCoordinator coordinator = makeBpfCoordinator();
- coordinator.startPolling();
-
- // The tether stats polling task should not be scheduled.
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- verify(mNetd, never()).tetherOffloadGetStats();
-
- // The interface name lookup table can't be added.
- final String iface = "rmnet_data0";
- final Integer ifIndex = 100;
- coordinator.addUpstreamNameToLookupTable(ifIndex, iface);
- assertEquals(0, coordinator.getInterfaceNamesForTesting().size());
-
- // The rule can't be added.
- final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
- final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a");
- final Ipv6ForwardingRule rule = buildTestForwardingRule(ifIndex, neigh, mac);
- coordinator.tetherOffloadRuleAdd(mIpServer, rule);
- verify(mNetd, never()).tetherOffloadRuleAdd(any());
- LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules =
- coordinator.getForwardingRulesForTesting().get(mIpServer);
- assertNull(rules);
-
- // The rule can't be removed. This is not a realistic case because adding rule is not
- // allowed. That implies no rule could be removed, cleared or updated. Verify these
- // cases just in case.
- rules = new LinkedHashMap<Inet6Address, Ipv6ForwardingRule>();
- rules.put(rule.address, rule);
- coordinator.getForwardingRulesForTesting().put(mIpServer, rules);
- coordinator.tetherOffloadRuleRemove(mIpServer, rule);
- verify(mNetd, never()).tetherOffloadRuleRemove(any());
- rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
- assertNotNull(rules);
- assertEquals(1, rules.size());
-
- // The rule can't be cleared.
- coordinator.tetherOffloadRuleClear(mIpServer);
- verify(mNetd, never()).tetherOffloadRuleRemove(any());
- rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
- assertNotNull(rules);
- assertEquals(1, rules.size());
-
- // The rule can't be updated.
- coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */);
- verify(mNetd, never()).tetherOffloadRuleRemove(any());
- verify(mNetd, never()).tetherOffloadRuleAdd(any());
- rules = coordinator.getForwardingRulesForTesting().get(mIpServer);
- assertNotNull(rules);
- assertEquals(1, rules.size());
- }
-
- @Test
- public void testTetheringConfigSetPollingInterval() throws Exception {
- setupFunctioningNetdInterface();
-
- final BpfCoordinator coordinator = makeBpfCoordinator();
-
- // [1] The default polling interval.
- coordinator.startPolling();
- assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval());
- coordinator.stopPolling();
-
- // [2] Expect the invalid polling interval isn't applied. The valid range of interval is
- // DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long.
- for (final int interval
- : new int[] {0, 100, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS - 1}) {
- when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval);
- coordinator.startPolling();
- assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval());
- coordinator.stopPolling();
- }
-
- // [3] Set a specific polling interval which is larger than default value.
- // Use a large polling interval to avoid flaky test because the time forwarding
- // approximation is used to verify the scheduled time of the polling thread.
- final int pollingInterval = 100_000;
- when(mTetherConfig.getOffloadPollInterval()).thenReturn(pollingInterval);
- coordinator.startPolling();
-
- // Expect the specific polling interval to be applied.
- assertEquals(pollingInterval, coordinator.getPollingInterval());
-
- // Start on a new polling time slot.
- mTestLooper.moveTimeForward(pollingInterval);
- waitForIdle();
- clearInvocations(mNetd);
-
- // Move time forward to 90% polling interval time. Expect that the polling thread has not
- // scheduled yet.
- mTestLooper.moveTimeForward((long) (pollingInterval * 0.9));
- waitForIdle();
- verify(mNetd, never()).tetherOffloadGetStats();
-
- // Move time forward to the remaining 10% polling interval time. Expect that the polling
- // thread has scheduled.
- mTestLooper.moveTimeForward((long) (pollingInterval * 0.1));
- waitForIdle();
- verify(mNetd).tetherOffloadGetStats();
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
deleted file mode 100644
index d915354b0c37..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
+++ /dev/null
@@ -1,162 +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.networkstack.tethering
-
-import android.net.LinkAddress
-import android.net.MacAddress
-import android.net.TetheredClient
-import android.net.TetheredClient.AddressInfo
-import android.net.TetheringManager.TETHERING_USB
-import android.net.TetheringManager.TETHERING_WIFI
-import android.net.ip.IpServer
-import android.net.wifi.WifiClient
-import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class ConnectedClientsTrackerTest {
-
- private val server1 = mock(IpServer::class.java)
- private val server2 = mock(IpServer::class.java)
- private val servers = listOf(server1, server2)
-
- private val clock = TestClock(1324L)
-
- private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A")
- private val client1 = TetheredClient(client1Addr, listOf(
- makeAddrInfo("192.168.43.44/32", null /* hostname */, clock.time + 20)),
- TETHERING_WIFI)
- private val wifiClient1 = makeWifiClient(client1Addr)
- private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB")
- private val client2Exp30AddrInfo = makeAddrInfo(
- "192.168.43.45/32", "my_hostname", clock.time + 30)
- private val client2 = TetheredClient(client2Addr, listOf(
- client2Exp30AddrInfo,
- makeAddrInfo("2001:db8:12::34/72", "other_hostname", clock.time + 10)),
- TETHERING_WIFI)
- private val wifiClient2 = makeWifiClient(client2Addr)
- private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC")
- private val client3 = TetheredClient(client3Addr,
- listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)),
- TETHERING_USB)
-
- private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) =
- LinkAddress(addr).let {
- AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope,
- expTime /* deprecationTime */, expTime /* expirationTime */), hostname)
- }
-
- @Test
- fun testUpdateConnectedClients() {
- doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
- doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
-
- val tracker = ConnectedClientsTracker(clock)
- assertFalse(tracker.updateConnectedClients(servers, null))
-
- // Obtain a lease for client 1
- doReturn(listOf(client1)).`when`(server1).allLeases
- assertSameClients(listOf(client1), assertNewClients(tracker, servers, listOf(wifiClient1)))
-
- // Client 2 L2-connected, no lease yet
- val client2WithoutAddr = TetheredClient(client2Addr, emptyList(), TETHERING_WIFI)
- assertSameClients(listOf(client1, client2WithoutAddr),
- assertNewClients(tracker, servers, listOf(wifiClient1, wifiClient2)))
-
- // Client 2 lease obtained
- doReturn(listOf(client1, client2)).`when`(server1).allLeases
- assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers, null))
-
- // Client 3 lease obtained
- doReturn(listOf(client3)).`when`(server2).allLeases
- assertSameClients(listOf(client1, client2, client3),
- assertNewClients(tracker, servers, null))
-
- // Client 2 L2-disconnected
- assertSameClients(listOf(client1, client3),
- assertNewClients(tracker, servers, listOf(wifiClient1)))
-
- // Client 1 L2-disconnected
- assertSameClients(listOf(client3), assertNewClients(tracker, servers, emptyList()))
-
- // Client 1 comes back
- assertSameClients(listOf(client1, client3),
- assertNewClients(tracker, servers, listOf(wifiClient1)))
-
- // Leases lost, client 1 still L2-connected
- doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
- doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
- assertSameClients(listOf(TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)),
- assertNewClients(tracker, servers, null))
- }
-
- @Test
- fun testUpdateConnectedClients_LeaseExpiration() {
- val tracker = ConnectedClientsTracker(clock)
- doReturn(listOf(client1, client2)).`when`(server1).allLeases
- doReturn(listOf(client3)).`when`(server2).allLeases
- assertSameClients(listOf(client1, client2, client3), assertNewClients(
- tracker, servers, listOf(wifiClient1, wifiClient2)))
-
- clock.time += 20
- // Client 3 has no remaining lease: removed
- val expectedClients = listOf(
- // Client 1 has no remaining lease but is L2-connected
- TetheredClient(client1Addr, emptyList(), TETHERING_WIFI),
- // Client 2 has some expired leases
- TetheredClient(
- client2Addr,
- // Only the "t + 30" address is left, the "t + 10" address expired
- listOf(client2Exp30AddrInfo),
- TETHERING_WIFI))
- assertSameClients(expectedClients, assertNewClients(tracker, servers, null))
- }
-
- private fun assertNewClients(
- tracker: ConnectedClientsTracker,
- ipServers: Iterable<IpServer>,
- wifiClients: List<WifiClient>?
- ): List<TetheredClient> {
- assertTrue(tracker.updateConnectedClients(ipServers, wifiClients))
- return tracker.lastTetheredClients
- }
-
- private fun assertSameClients(expected: List<TetheredClient>, actual: List<TetheredClient>) {
- val expectedSet = HashSet(expected)
- assertEquals(expected.size, expectedSet.size)
- assertEquals(expectedSet, HashSet(actual))
- }
-
- private fun makeWifiClient(macAddr: MacAddress): WifiClient {
- // Use a mock WifiClient as the constructor is not part of the WiFi module exported API.
- return mock(WifiClient::class.java).apply { doReturn(macAddr).`when`(this).macAddress }
- }
-
- private class TestClock(var time: Long) : ConnectedClientsTracker.Clock() {
- override fun elapsedRealtime(): Long {
- return time
- }
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
deleted file mode 100644
index 354e75356e9f..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
-import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
-import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
-import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
-import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
-import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
-import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
-import static android.net.TetheringManager.TETHERING_BLUETOOTH;
-import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringManager.TETHERING_INVALID;
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
-import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.net.util.SharedLog;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.PersistableBundle;
-import android.os.ResultReceiver;
-import android.os.SystemProperties;
-import android.os.test.TestLooper;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.test.BroadcastInterceptingContext;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public final class EntitlementManagerTest {
-
- private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
- private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
- private static final String PROVISIONING_APP_RESPONSE = "app_response";
-
- @Mock private CarrierConfigManager mCarrierConfigManager;
- @Mock private Context mContext;
- @Mock private Resources mResources;
- @Mock private SharedLog mLog;
- @Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener;
-
- // Like so many Android system APIs, these cannot be mocked because it is marked final.
- // We have to use the real versions.
- private final PersistableBundle mCarrierConfig = new PersistableBundle();
- private final TestLooper mLooper = new TestLooper();
- private Context mMockContext;
- private Runnable mPermissionChangeCallback;
-
- private WrappedEntitlementManager mEnMgr;
- private TetheringConfiguration mConfig;
- private MockitoSession mMockingSession;
-
- private class MockContext extends BroadcastInterceptingContext {
- MockContext(Context base) {
- super(base);
- }
-
- @Override
- public Resources getResources() {
- return mResources;
- }
- }
-
- public class WrappedEntitlementManager extends EntitlementManager {
- public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN;
- public int uiProvisionCount = 0;
- public int silentProvisionCount = 0;
-
- public WrappedEntitlementManager(Context ctx, Handler h, SharedLog log,
- Runnable callback) {
- super(ctx, h, log, callback);
- }
-
- public void reset() {
- fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN;
- uiProvisionCount = 0;
- silentProvisionCount = 0;
- }
-
- @Override
- protected Intent runUiTetherProvisioning(int type,
- final TetheringConfiguration config, final ResultReceiver receiver) {
- Intent intent = super.runUiTetherProvisioning(type, config, receiver);
- assertUiTetherProvisioningIntent(type, config, receiver, intent);
- uiProvisionCount++;
- receiver.send(fakeEntitlementResult, null);
- return intent;
- }
-
- private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config,
- final ResultReceiver receiver, final Intent intent) {
- assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction());
- assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
- final String[] appName = intent.getStringArrayExtra(
- EXTRA_TETHER_UI_PROVISIONING_APP_NAME);
- assertEquals(PROVISIONING_APP_NAME.length, appName.length);
- for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) {
- assertEquals(PROVISIONING_APP_NAME[i], appName[i]);
- }
- assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK));
- assertEquals(config.activeDataSubId,
- intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
- }
-
- @Override
- protected Intent runSilentTetherProvisioning(int type,
- final TetheringConfiguration config) {
- Intent intent = super.runSilentTetherProvisioning(type, config);
- assertSilentTetherProvisioning(type, config, intent);
- silentProvisionCount++;
- addDownstreamMapping(type, fakeEntitlementResult);
- return intent;
- }
-
- private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config,
- final Intent intent) {
- assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
- assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false));
- assertEquals(PROVISIONING_NO_UI_APP_NAME,
- intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION));
- assertEquals(PROVISIONING_APP_RESPONSE,
- intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE));
- assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK));
- assertEquals(config.activeDataSubId,
- intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
- }
- }
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mMockingSession = mockitoSession()
- .initMocks(this)
- .mockStatic(SystemProperties.class)
- .mockStatic(DeviceConfig.class)
- .strictness(Strictness.WARN)
- .startMocking();
- // Don't disable tethering provisioning unless requested.
- doReturn(false).when(
- () -> SystemProperties.getBoolean(
- eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
- doReturn(null).when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString()));
-
- when(mResources.getStringArray(R.array.config_tether_dhcp_range))
- .thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_usb_regexs))
- .thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
- .thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
- when(mResources.getIntArray(R.array.config_tether_upstream_types))
- .thenReturn(new int[0]);
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
- false);
- when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
- when(mLog.forSubComponent(anyString())).thenReturn(mLog);
-
- mMockContext = new MockContext(mContext);
- mPermissionChangeCallback = spy(() -> { });
- mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog,
- mPermissionChangeCallback);
- mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
- mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- mEnMgr.setTetheringConfigurationFetcher(() -> {
- return mConfig;
- });
- }
-
- @After
- public void tearDown() throws Exception {
- mMockingSession.finishMocking();
- }
-
- private void setupForRequiredProvisioning() {
- // Produce some acceptable looking provision app setting if requested.
- when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
- .thenReturn(PROVISIONING_APP_NAME);
- when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
- .thenReturn(PROVISIONING_NO_UI_APP_NAME);
- when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn(
- PROVISIONING_APP_RESPONSE);
- // Act like the CarrierConfigManager is present and ready unless told otherwise.
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(mCarrierConfigManager);
- when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
- mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- }
-
- @Test
- public void canRequireProvisioning() {
- setupForRequiredProvisioning();
- assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
- }
-
- @Test
- public void toleratesCarrierConfigManagerMissing() {
- setupForRequiredProvisioning();
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(null);
- mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
- // Therefore provisioning still be required.
- assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
- }
-
- @Test
- public void toleratesCarrierConfigMissing() {
- setupForRequiredProvisioning();
- when(mCarrierConfigManager.getConfig()).thenReturn(null);
- mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- // We still have a provisioning app configured, so still require provisioning.
- assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
- }
-
- @Test
- public void toleratesCarrierConfigNotLoaded() {
- setupForRequiredProvisioning();
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
- // We still have a provisioning app configured, so still require provisioning.
- assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
- }
-
- @Test
- public void provisioningNotRequiredWhenAppNotFound() {
- setupForRequiredProvisioning();
- when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
- .thenReturn(null);
- mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
- when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
- .thenReturn(new String[] {"malformedApp"});
- mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
- }
-
- @Test
- public void testRequestLastEntitlementCacheValue() throws Exception {
- // 1. Entitlement check is not required.
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- ResultReceiver receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- }
- };
- mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- mEnMgr.reset();
-
- setupForRequiredProvisioning();
- // 2. No cache value and don't need to run entitlement check.
- receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- }
- };
- mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- mEnMgr.reset();
- // 3. No cache value and ui entitlement check is needed.
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
- }
- };
- mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
- mLooper.dispatchAll();
- assertEquals(1, mEnMgr.uiProvisionCount);
- mEnMgr.reset();
- // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement
- // check.
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
- }
- };
- mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- mEnMgr.reset();
- // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed.
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- }
- };
- mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
- mLooper.dispatchAll();
- assertEquals(1, mEnMgr.uiProvisionCount);
- mEnMgr.reset();
- // 6. Cache value is TETHER_ERROR_NO_ERROR.
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- }
- };
- mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- mEnMgr.reset();
- // 7. Test get value for other downstream type.
- receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- }
- };
- mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- mEnMgr.reset();
- // 8. Test get value for invalid downstream type.
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- }
- };
- mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- mEnMgr.reset();
- }
-
- private void assertPermissionChangeCallback(InOrder inOrder) {
- inOrder.verify(mPermissionChangeCallback, times(1)).run();
- }
-
- private void assertNoPermissionChange(InOrder inOrder) {
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void verifyPermissionResult() {
- final InOrder inOrder = inOrder(mPermissionChangeCallback);
- setupForRequiredProvisioning();
- mEnMgr.notifyUpstream(true);
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
- mLooper.dispatchAll();
- // Permitted: true -> false
- assertPermissionChangeCallback(inOrder);
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
-
- mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
- mLooper.dispatchAll();
- // Permitted: false -> false
- assertNoPermissionChange(inOrder);
-
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
- mLooper.dispatchAll();
- // Permitted: false -> true
- assertPermissionChangeCallback(inOrder);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
- }
-
- @Test
- public void verifyPermissionIfAllNotApproved() {
- final InOrder inOrder = inOrder(mPermissionChangeCallback);
- setupForRequiredProvisioning();
- mEnMgr.notifyUpstream(true);
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
- mLooper.dispatchAll();
- // Permitted: true -> false
- assertPermissionChangeCallback(inOrder);
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
-
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
- mLooper.dispatchAll();
- // Permitted: false -> false
- assertNoPermissionChange(inOrder);
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
-
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
- mLooper.dispatchAll();
- // Permitted: false -> false
- assertNoPermissionChange(inOrder);
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
- }
-
- @Test
- public void verifyPermissionIfAnyApproved() {
- final InOrder inOrder = inOrder(mPermissionChangeCallback);
- setupForRequiredProvisioning();
- mEnMgr.notifyUpstream(true);
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
- mLooper.dispatchAll();
- // Permitted: true -> true
- assertNoPermissionChange(inOrder);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
-
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
- mLooper.dispatchAll();
- // Permitted: true -> true
- assertNoPermissionChange(inOrder);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
-
- mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
- mLooper.dispatchAll();
- // Permitted: true -> false
- assertPermissionChangeCallback(inOrder);
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
- }
-
- @Test
- public void verifyPermissionWhenProvisioningNotStarted() {
- final InOrder inOrder = inOrder(mPermissionChangeCallback);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
- assertNoPermissionChange(inOrder);
- setupForRequiredProvisioning();
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
- assertNoPermissionChange(inOrder);
- }
-
- @Test
- public void testRunTetherProvisioning() {
- final InOrder inOrder = inOrder(mPermissionChangeCallback);
- setupForRequiredProvisioning();
- // 1. start ui provisioning, upstream is mobile
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- mEnMgr.notifyUpstream(true);
- mLooper.dispatchAll();
- mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
- mLooper.dispatchAll();
- assertEquals(1, mEnMgr.uiProvisionCount);
- assertEquals(0, mEnMgr.silentProvisionCount);
- // Permitted: true -> true
- assertNoPermissionChange(inOrder);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
- mEnMgr.reset();
-
- // 2. start no-ui provisioning
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- assertEquals(1, mEnMgr.silentProvisionCount);
- // Permitted: true -> true
- assertNoPermissionChange(inOrder);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
- mEnMgr.reset();
-
- // 3. tear down mobile, then start ui provisioning
- mEnMgr.notifyUpstream(false);
- mLooper.dispatchAll();
- mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- assertEquals(0, mEnMgr.silentProvisionCount);
- assertNoPermissionChange(inOrder);
- mEnMgr.reset();
-
- // 4. switch upstream back to mobile
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- mEnMgr.notifyUpstream(true);
- mLooper.dispatchAll();
- assertEquals(1, mEnMgr.uiProvisionCount);
- assertEquals(0, mEnMgr.silentProvisionCount);
- // Permitted: true -> true
- assertNoPermissionChange(inOrder);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
- mEnMgr.reset();
-
- // 5. tear down mobile, then switch SIM
- mEnMgr.notifyUpstream(false);
- mLooper.dispatchAll();
- mEnMgr.reevaluateSimCardProvisioning(mConfig);
- assertEquals(0, mEnMgr.uiProvisionCount);
- assertEquals(0, mEnMgr.silentProvisionCount);
- assertNoPermissionChange(inOrder);
- mEnMgr.reset();
-
- // 6. switch upstream back to mobile again
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- mEnMgr.notifyUpstream(true);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- assertEquals(3, mEnMgr.silentProvisionCount);
- // Permitted: true -> false
- assertPermissionChangeCallback(inOrder);
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
- mEnMgr.reset();
-
- // 7. start ui provisioning, upstream is mobile, downstream is ethernet
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true);
- mLooper.dispatchAll();
- assertEquals(1, mEnMgr.uiProvisionCount);
- assertEquals(0, mEnMgr.silentProvisionCount);
- // Permitted: false -> true
- assertPermissionChangeCallback(inOrder);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
- mEnMgr.reset();
-
- // 8. downstream is invalid
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
- mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true);
- mLooper.dispatchAll();
- assertEquals(0, mEnMgr.uiProvisionCount);
- assertEquals(0, mEnMgr.silentProvisionCount);
- assertNoPermissionChange(inOrder);
- mEnMgr.reset();
- }
-
- @Test
- public void testCallStopTetheringWhenUiProvisioningFail() {
- setupForRequiredProvisioning();
- verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI);
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- mEnMgr.notifyUpstream(true);
- mLooper.dispatchAll();
- mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
- mLooper.dispatchAll();
- assertEquals(1, mEnMgr.uiProvisionCount);
- verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI);
- }
-
- @Test
- public void testsetExemptedDownstreamType() throws Exception {
- setupForRequiredProvisioning();
- // Cellular upstream is not permitted when no entitlement result.
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
-
- // If there is exempted downstream and no other non-exempted downstreams, cellular is
- // permitted.
- mEnMgr.setExemptedDownstreamType(TETHERING_WIFI);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
-
- // If second downstream run entitlement check fail, cellular upstream is not permitted.
- mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
- mEnMgr.notifyUpstream(true);
- mLooper.dispatchAll();
- mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
- mLooper.dispatchAll();
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
-
- // When second downstream is down, exempted downstream can use cellular upstream.
- assertEquals(1, mEnMgr.uiProvisionCount);
- verify(mEntitlementFailedListener).onUiEntitlementFailed(TETHERING_USB);
- mEnMgr.stopProvisioningIfNeeded(TETHERING_USB);
- assertTrue(mEnMgr.isCellularUpstreamPermitted());
-
- mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
- assertFalse(mEnMgr.isCellularUpstreamPermitted());
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
deleted file mode 100644
index f2b5314e5a17..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
+++ /dev/null
@@ -1,156 +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.networkstack.tethering;
-
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
-import static android.net.ip.IpServer.STATE_TETHERED;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.net.InetAddresses;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.RouteInfo;
-import android.net.ip.IpServer;
-import android.net.util.SharedLog;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class IPv6TetheringCoordinatorTest {
- private static final String TEST_DNS_SERVER = "2001:4860:4860::8888";
- private static final String TEST_INTERFACE = "test_rmnet0";
- private static final String TEST_IPV6_ADDRESS = "2001:db8::1/64";
- private static final String TEST_IPV4_ADDRESS = "192.168.100.1/24";
-
- private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
- private ArrayList<IpServer> mNotifyList;
-
- @Mock private SharedLog mSharedLog;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
- mNotifyList = new ArrayList<IpServer>();
- mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mSharedLog);
- }
-
- private UpstreamNetworkState createDualStackUpstream(final int transportType) {
- final Network network = mock(Network.class);
- final NetworkCapabilities netCap =
- new NetworkCapabilities.Builder().addTransportType(transportType).build();
- final InetAddress dns = InetAddresses.parseNumericAddress(TEST_DNS_SERVER);
- final LinkProperties linkProp = new LinkProperties();
- linkProp.setInterfaceName(TEST_INTERFACE);
- linkProp.addLinkAddress(new LinkAddress(TEST_IPV6_ADDRESS));
- linkProp.addLinkAddress(new LinkAddress(TEST_IPV4_ADDRESS));
- linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, TEST_INTERFACE, RTN_UNICAST));
- linkProp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, TEST_INTERFACE,
- RTN_UNICAST));
- linkProp.addDnsServer(dns);
- return new UpstreamNetworkState(linkProp, netCap, network);
- }
-
- private void assertOnlyOneV6AddressAndNoV4(LinkProperties lp) {
- assertEquals(lp.getInterfaceName(), TEST_INTERFACE);
- assertFalse(lp.hasIpv4Address());
- final List<LinkAddress> addresses = lp.getLinkAddresses();
- assertEquals(addresses.size(), 1);
- final LinkAddress v6Address = addresses.get(0);
- assertEquals(v6Address, new LinkAddress(TEST_IPV6_ADDRESS));
- }
-
- @Test
- public void testUpdateIpv6Upstream() throws Exception {
- // 1. Add first IpServer.
- final IpServer firstServer = mock(IpServer.class);
- mNotifyList.add(firstServer);
- mIPv6TetheringCoordinator.addActiveDownstream(firstServer, STATE_TETHERED);
- verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
- verifyNoMoreInteractions(firstServer);
-
- // 2. Add second IpServer and it would not have ipv6 tethering.
- final IpServer secondServer = mock(IpServer.class);
- mNotifyList.add(secondServer);
- mIPv6TetheringCoordinator.addActiveDownstream(secondServer, STATE_LOCAL_ONLY);
- verifyNoMoreInteractions(secondServer);
- reset(firstServer, secondServer);
-
- // 3. No upstream.
- mIPv6TetheringCoordinator.updateUpstreamNetworkState(null);
- verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
- reset(firstServer, secondServer);
-
- // 4. Update ipv6 mobile upstream.
- final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR);
- final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class);
- mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream);
- verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0),
- lp.capture());
- final LinkProperties v6OnlyLink = lp.getValue();
- assertOnlyOneV6AddressAndNoV4(v6OnlyLink);
- verifyNoMoreInteractions(firstServer);
- verifyNoMoreInteractions(secondServer);
- reset(firstServer, secondServer);
-
- // 5. Remove first IpServer.
- mNotifyList.remove(firstServer);
- mIPv6TetheringCoordinator.removeActiveDownstream(firstServer);
- verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
- verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0),
- lp.capture());
- final LinkProperties localOnlyLink = lp.getValue();
- assertNotNull(localOnlyLink);
- assertNotEquals(localOnlyLink, v6OnlyLink);
- reset(firstServer, secondServer);
-
- // 6. Remove second IpServer.
- mNotifyList.remove(secondServer);
- mIPv6TetheringCoordinator.removeActiveDownstream(secondServer);
- verifyNoMoreInteractions(firstServer);
- verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
deleted file mode 100644
index 071a290e657b..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
+++ /dev/null
@@ -1,72 +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.networkstack.tethering;
-
-import static android.Manifest.permission.WRITE_SETTINGS;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import static org.mockito.Mockito.mock;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.ITetheringConnector;
-import android.os.Binder;
-import android.os.IBinder;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public class MockTetheringService extends TetheringService {
- private final Tethering mTethering = mock(Tethering.class);
-
- @Override
- public IBinder onBind(Intent intent) {
- return new MockTetheringConnector(super.onBind(intent));
- }
-
- @Override
- public Tethering makeTethering(TetheringDependencies deps) {
- return mTethering;
- }
-
- @Override
- boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
- @NonNull String callingPackage, @Nullable String callingAttributionTag,
- boolean throwException) {
- // Test this does not verify the calling package / UID, as calling package could be shell
- // and not match the UID.
- return context.checkCallingOrSelfPermission(WRITE_SETTINGS) == PERMISSION_GRANTED;
- }
-
- public Tethering getTethering() {
- return mTethering;
- }
-
- public class MockTetheringConnector extends Binder {
- final IBinder mBase;
- MockTetheringConnector(IBinder base) {
- mBase = base;
- }
-
- public ITetheringConnector getTetheringConnector() {
- return ITetheringConnector.Stub.asInterface(mBase);
- }
-
- public MockTetheringService getService() {
- return MockTetheringService.this;
- }
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
deleted file mode 100644
index ce52ae22ece8..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
+++ /dev/null
@@ -1,827 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStats.UID_TETHERING;
-import static android.net.RouteInfo.RTN_UNICAST;
-import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
-
-import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE;
-import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
-import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
-import static com.android.testutils.MiscAsserts.assertContainsAll;
-import static com.android.testutils.MiscAsserts.assertThrows;
-import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals;
-
-import static junit.framework.Assert.assertNotNull;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.usage.NetworkStatsManager;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.net.ITetheringStatsProvider;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NetworkStats;
-import android.net.NetworkStats.Entry;
-import android.net.RouteInfo;
-import android.net.netstats.provider.NetworkStatsProvider;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.test.TestLooper;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.test.mock.MockContentResolver;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.testutils.TestableNetworkStatsProviderCbBinder;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class OffloadControllerTest {
- private static final String RNDIS0 = "test_rndis0";
- private static final String RMNET0 = "test_rmnet_data0";
- private static final String WLAN0 = "test_wlan0";
-
- private static final String IPV6_LINKLOCAL = "fe80::/64";
- private static final String IPV6_DOC_PREFIX = "2001:db8::/64";
- private static final String IPV6_DISCARD_PREFIX = "100::/64";
- private static final String USB_PREFIX = "192.168.42.0/24";
- private static final String WIFI_PREFIX = "192.168.43.0/24";
- private static final long WAIT_FOR_IDLE_TIMEOUT = 2 * 1000;
-
- @Mock private OffloadHardwareInterface mHardware;
- @Mock private ApplicationInfo mApplicationInfo;
- @Mock private Context mContext;
- @Mock private NetworkStatsManager mStatsManager;
- @Mock private TetheringConfiguration mTetherConfig;
- // Late init since methods must be called by the thread that created this object.
- private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
- private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider;
- private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
- ArgumentCaptor.forClass(ArrayList.class);
- private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
- ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
- private MockContentResolver mContentResolver;
- private final TestLooper mTestLooper = new TestLooper();
- private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() {
- @Override
- public TetheringConfiguration getTetherConfig() {
- return mTetherConfig;
- }
- };
-
- @Before public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
- when(mContext.getPackageName()).thenReturn("OffloadControllerTest");
- mContentResolver = new MockContentResolver(mContext);
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContext.getContentResolver()).thenReturn(mContentResolver);
- FakeSettingsProvider.clearSettingsProvider();
- when(mTetherConfig.getOffloadPollInterval()).thenReturn(-1); // Disabled.
- }
-
- @After public void tearDown() throws Exception {
- FakeSettingsProvider.clearSettingsProvider();
- }
-
- private void setupFunctioningHardwareInterface() {
- when(mHardware.initOffloadConfig()).thenReturn(true);
- when(mHardware.initOffloadControl(mControlCallbackCaptor.capture()))
- .thenReturn(true);
- when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true);
- when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
- when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
- }
-
- private void enableOffload() {
- Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
- }
-
- private void setOffloadPollInterval(int interval) {
- when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval);
- }
-
- private void waitForIdle() {
- mTestLooper.dispatchAll();
- }
-
- private OffloadController makeOffloadController() throws Exception {
- OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()),
- mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps);
- final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
- tetherStatsProviderCaptor =
- ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
- verify(mStatsManager).registerNetworkStatsProvider(anyString(),
- tetherStatsProviderCaptor.capture());
- mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
- assertNotNull(mTetherStatsProvider);
- mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
- mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
- return offload;
- }
-
- @Test
- public void testNoSettingsValueDefaultDisabledDoesNotStart() throws Exception {
- setupFunctioningHardwareInterface();
- when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1);
- assertThrows(SettingNotFoundException.class, () ->
- Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED));
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- final InOrder inOrder = inOrder(mHardware);
- inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled();
- inOrder.verify(mHardware, never()).initOffloadConfig();
- inOrder.verify(mHardware, never()).initOffloadControl(
- any(OffloadHardwareInterface.ControlCallback.class));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testNoSettingsValueDefaultEnabledDoesStart() throws Exception {
- setupFunctioningHardwareInterface();
- when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0);
- assertThrows(SettingNotFoundException.class, () ->
- Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED));
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- final InOrder inOrder = inOrder(mHardware);
- inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled();
- inOrder.verify(mHardware, times(1)).initOffloadConfig();
- inOrder.verify(mHardware, times(1)).initOffloadControl(
- any(OffloadHardwareInterface.ControlCallback.class));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testSettingsAllowsStart() throws Exception {
- setupFunctioningHardwareInterface();
- Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- final InOrder inOrder = inOrder(mHardware);
- inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled();
- inOrder.verify(mHardware, times(1)).initOffloadConfig();
- inOrder.verify(mHardware, times(1)).initOffloadControl(
- any(OffloadHardwareInterface.ControlCallback.class));
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testSettingsDisablesStart() throws Exception {
- setupFunctioningHardwareInterface();
- Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1);
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- final InOrder inOrder = inOrder(mHardware);
- inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled();
- inOrder.verify(mHardware, never()).initOffloadConfig();
- inOrder.verify(mHardware, never()).initOffloadControl(anyObject());
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testSetUpstreamLinkPropertiesWorking() throws Exception {
- setupFunctioningHardwareInterface();
- enableOffload();
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- final InOrder inOrder = inOrder(mHardware);
- inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled();
- inOrder.verify(mHardware, times(1)).initOffloadConfig();
- inOrder.verify(mHardware, times(1)).initOffloadControl(
- any(OffloadHardwareInterface.ControlCallback.class));
- inOrder.verifyNoMoreInteractions();
-
- // In reality, the UpstreamNetworkMonitor would have passed down to us
- // a covering set of local prefixes representing a minimum essential
- // set plus all the prefixes on networks with network agents.
- //
- // We simulate that there, and then add upstream elements one by one
- // and watch what happens.
- final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>();
- for (String s : new String[]{
- "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) {
- minimumLocalPrefixes.add(new IpPrefix(s));
- }
- offload.setLocalPrefixes(minimumLocalPrefixes);
- inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
- ArrayList<String> localPrefixes = mStringArrayCaptor.getValue();
- assertEquals(4, localPrefixes.size());
- assertContainsAll(localPrefixes,
- "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64");
- inOrder.verifyNoMoreInteractions();
-
- offload.setUpstreamLinkProperties(null);
- // No change in local addresses means no call to setLocalPrefixes().
- inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
- // This LinkProperties value does not differ from the default upstream.
- // There should be no extraneous call to setUpstreamParameters().
- inOrder.verify(mHardware, never()).setUpstreamParameters(
- anyObject(), anyObject(), anyObject(), anyObject());
- inOrder.verifyNoMoreInteractions();
-
- final LinkProperties lp = new LinkProperties();
-
- final String testIfName = "rmnet_data17";
- lp.setInterfaceName(testIfName);
- offload.setUpstreamLinkProperties(lp);
- // No change in local addresses means no call to setLocalPrefixes().
- inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(null), eq(null), eq(null));
- inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE));
- inOrder.verifyNoMoreInteractions();
-
- final String ipv4Addr = "192.0.2.5";
- final String linkAddr = ipv4Addr + "/24";
- lp.addLinkAddress(new LinkAddress(linkAddr));
- lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, null, RTN_UNICAST));
- offload.setUpstreamLinkProperties(lp);
- // IPv4 prefixes and addresses on the upstream are simply left as whole
- // prefixes (already passed in from UpstreamNetworkMonitor code). If a
- // tethering client sends traffic to the IPv4 default router or other
- // clients on the upstream this will not be hardware-forwarded, and that
- // should be fine for now. Ergo: no change in local addresses, no call
- // to setLocalPrefixes().
- inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(ipv4Addr), eq(null), eq(null));
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
- inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE));
- inOrder.verifyNoMoreInteractions();
-
- final String ipv4Gateway = "192.0.2.1";
- lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv4Gateway), null, RTN_UNICAST));
- offload.setUpstreamLinkProperties(lp);
- // No change in local addresses means no call to setLocalPrefixes().
- inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null));
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
- inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE));
- inOrder.verifyNoMoreInteractions();
-
- final String ipv6Gw1 = "fe80::cafe";
- lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw1), null, RTN_UNICAST));
- offload.setUpstreamLinkProperties(lp);
- // No change in local addresses means no call to setLocalPrefixes().
- inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
- ArrayList<String> v6gws = mStringArrayCaptor.getValue();
- assertEquals(1, v6gws.size());
- assertTrue(v6gws.contains(ipv6Gw1));
- inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE));
- inOrder.verifyNoMoreInteractions();
-
- final String ipv6Gw2 = "fe80::d00d";
- lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw2), null, RTN_UNICAST));
- offload.setUpstreamLinkProperties(lp);
- // No change in local addresses means no call to setLocalPrefixes().
- inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
- v6gws = mStringArrayCaptor.getValue();
- assertEquals(2, v6gws.size());
- assertTrue(v6gws.contains(ipv6Gw1));
- assertTrue(v6gws.contains(ipv6Gw2));
- inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE));
- inOrder.verifyNoMoreInteractions();
-
- final LinkProperties stacked = new LinkProperties();
- stacked.setInterfaceName("stacked");
- stacked.addLinkAddress(new LinkAddress("192.0.2.129/25"));
- stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null,
- RTN_UNICAST));
- stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null,
- RTN_UNICAST));
- assertTrue(lp.addStackedLink(stacked));
- offload.setUpstreamLinkProperties(lp);
- // No change in local addresses means no call to setLocalPrefixes().
- inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
- v6gws = mStringArrayCaptor.getValue();
- assertEquals(2, v6gws.size());
- assertTrue(v6gws.contains(ipv6Gw1));
- assertTrue(v6gws.contains(ipv6Gw2));
- inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE));
- inOrder.verifyNoMoreInteractions();
-
- // Add in some IPv6 upstream info. When there is a tethered downstream
- // making use of the IPv6 prefix we would expect to see the /64 route
- // removed from "local prefixes" and /128s added for the upstream IPv6
- // addresses. This is not yet implemented, and for now we simply
- // expect to see these /128s.
- lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null, RTN_UNICAST));
- // "2001:db8::/64" plus "assigned" ASCII in hex
- lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64"));
- // "2001:db8::/64" plus "random" ASCII in hex
- lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64"));
- offload.setUpstreamLinkProperties(lp);
- inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
- localPrefixes = mStringArrayCaptor.getValue();
- assertEquals(6, localPrefixes.size());
- assertContainsAll(localPrefixes,
- "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64",
- "2001:db8::6173:7369:676e:6564/128", "2001:db8::7261:6e64:6f6d/128");
- // The relevant parts of the LinkProperties have not changed, but at the
- // moment we do not de-dup upstream LinkProperties this carefully.
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
- v6gws = mStringArrayCaptor.getValue();
- assertEquals(2, v6gws.size());
- assertTrue(v6gws.contains(ipv6Gw1));
- assertTrue(v6gws.contains(ipv6Gw2));
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
- inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE));
- inOrder.verifyNoMoreInteractions();
-
- // Completely identical LinkProperties updates are de-duped.
- offload.setUpstreamLinkProperties(lp);
- // This LinkProperties value does not differ from the default upstream.
- // There should be no extraneous call to setUpstreamParameters().
- inOrder.verify(mHardware, never()).setUpstreamParameters(
- anyObject(), anyObject(), anyObject(), anyObject());
- inOrder.verifyNoMoreInteractions();
- }
-
- private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how,
- @NonNull String iface, long rxBytes, long txBytes) {
- return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT,
- TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L,
- txBytes, 0L, 0L);
- }
-
- @Test
- public void testGetForwardedStats() throws Exception {
- setupFunctioningHardwareInterface();
- enableOffload();
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- final String ethernetIface = "eth1";
- final String mobileIface = "rmnet_data0";
-
- when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
- new ForwardedStats(12345, 54321));
- when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(
- new ForwardedStats(999, 99999));
-
- InOrder inOrder = inOrder(mHardware);
-
- final LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
- // Previous upstream was null, so no stats are fetched.
- inOrder.verify(mHardware, never()).getForwardedStats(any());
-
- lp.setInterfaceName(mobileIface);
- offload.setUpstreamLinkProperties(lp);
- // Expect that we fetch stats from the previous upstream.
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
-
- lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
- // Expect that we fetch stats from the previous upstream.
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
-
- // Verify that the fetched stats are stored.
- final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
- final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
- final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
- .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
-
- final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
- .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
-
- assertNetworkStatsEquals(expectedIfaceStats, ifaceStats);
- assertNetworkStatsEquals(expectedUidStats, uidStats);
-
- // Force pushing stats update to verify the stats reported.
- mTetherStatsProvider.pushTetherStats();
- mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats);
-
- when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
- new ForwardedStats(100000, 100000));
- offload.setUpstreamLinkProperties(null);
- // Expect that we first clear the HAL's upstream parameters.
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(""), eq("0.0.0.0"), eq("0.0.0.0"), eq(null));
- // Expect that we fetch stats from the previous upstream.
- inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
-
- // There is no current upstream, so no stats are fetched.
- inOrder.verify(mHardware, never()).getForwardedStats(any());
- inOrder.verifyNoMoreInteractions();
-
- // Verify that the stored stats is accumulated.
- final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
- final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
- final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
- .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
-
- final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
- .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
-
- assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu);
- assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu);
-
- // Verify that only diff of stats is reported.
- mTetherStatsProvider.pushTetherStats();
- final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
- .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
-
- final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
- .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
- .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
- mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff,
- expectedUidStatsDiff);
- }
-
- @Test
- public void testSetInterfaceQuota() throws Exception {
- setupFunctioningHardwareInterface();
- enableOffload();
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- final String ethernetIface = "eth1";
- final String mobileIface = "rmnet_data0";
- final long ethernetLimit = 12345;
- final long mobileLimit = 12345678;
-
- final LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
-
- final InOrder inOrder = inOrder(mHardware);
- when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
- when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
-
- // Applying an interface quota to the current upstream immediately sends it to the hardware.
- mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
- waitForIdle();
- inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
- inOrder.verifyNoMoreInteractions();
-
- // Applying an interface quota to another upstream does not take any immediate action.
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
- waitForIdle();
- inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
-
- // Switching to that upstream causes the quota to be applied if the parameters were applied
- // correctly.
- lp.setInterfaceName(mobileIface);
- offload.setUpstreamLinkProperties(lp);
- waitForIdle();
- inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit);
-
- // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
- // to Long.MAX_VALUE.
- mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
- waitForIdle();
- inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
-
- // If setting upstream parameters fails, then the data limit is not set.
- when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
- lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
- waitForIdle();
- inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
-
- // If setting the data limit fails while changing upstreams, offload is stopped.
- when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
- when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
- lp.setInterfaceName(mobileIface);
- offload.setUpstreamLinkProperties(lp);
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
- waitForIdle();
- inOrder.verify(mHardware).getForwardedStats(ethernetIface);
- inOrder.verify(mHardware).stopOffloadControl();
- }
-
- @Test
- public void testDataLimitCallback() throws Exception {
- setupFunctioningHardwareInterface();
- enableOffload();
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
- callback.onStoppedLimitReached();
- mTetherStatsProviderCb.expectNotifyStatsUpdated();
- }
-
- @Test
- public void testAddRemoveDownstreams() throws Exception {
- setupFunctioningHardwareInterface();
- enableOffload();
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- final InOrder inOrder = inOrder(mHardware);
- inOrder.verify(mHardware, times(1)).initOffloadConfig();
- inOrder.verify(mHardware, times(1)).initOffloadControl(
- any(OffloadHardwareInterface.ControlCallback.class));
- inOrder.verifyNoMoreInteractions();
-
- // Tethering makes several calls to setLocalPrefixes() before add/remove
- // downstream calls are made. This is not tested here; only the behavior
- // of notifyDownstreamLinkProperties() and removeDownstreamInterface()
- // are tested.
-
- // [1] USB tethering is started.
- final LinkProperties usbLinkProperties = new LinkProperties();
- usbLinkProperties.setInterfaceName(RNDIS0);
- usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24"));
- usbLinkProperties.addRoute(
- new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST));
- offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX);
- inOrder.verifyNoMoreInteractions();
-
- // [2] Routes for IPv6 link-local prefixes should never be added.
- usbLinkProperties.addRoute(
- new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST));
- offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString());
- inOrder.verifyNoMoreInteractions();
-
- // [3] Add an IPv6 prefix for good measure. Only new offload-able
- // prefixes should be passed to the HAL.
- usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
- usbLinkProperties.addRoute(
- new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST));
- offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX);
- inOrder.verifyNoMoreInteractions();
-
- // [4] Adding addresses doesn't affect notifyDownstreamLinkProperties().
- // The address is passed in by a separate setLocalPrefixes() invocation.
- usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::2/64"));
- offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString());
-
- // [5] Differences in local routes are converted into addDownstream()
- // and removeDownstream() invocations accordingly.
- usbLinkProperties.removeRoute(
- new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0, RTN_UNICAST));
- usbLinkProperties.addRoute(
- new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST));
- offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX);
- inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX);
- inOrder.verifyNoMoreInteractions();
-
- // [6] Removing a downstream interface which was never added causes no
- // interactions with the HAL.
- offload.removeDownstreamInterface(WLAN0);
- inOrder.verifyNoMoreInteractions();
-
- // [7] Removing an active downstream removes all remaining prefixes.
- offload.removeDownstreamInterface(RNDIS0);
- inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, USB_PREFIX);
- inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX);
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception {
- setupFunctioningHardwareInterface();
- enableOffload();
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- // Pretend to set a few different upstreams (only the interface name
- // matters for this test; we're ignoring IP and route information).
- final LinkProperties upstreamLp = new LinkProperties();
- for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) {
- upstreamLp.setInterfaceName(ifname);
- offload.setUpstreamLinkProperties(upstreamLp);
- }
-
- // Clear invocation history, especially the getForwardedStats() calls
- // that happen with setUpstreamParameters().
- clearInvocations(mHardware);
-
- OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
- callback.onStoppedUnsupported();
-
- // Verify forwarded stats behaviour.
- verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
- verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
- // TODO: verify the exact stats reported.
- mTetherStatsProviderCb.expectNotifyStatsUpdated();
- mTetherStatsProviderCb.assertNoCallback();
- verifyNoMoreInteractions(mHardware);
- }
-
- @Test
- public void testControlCallbackOnSupportAvailableFetchesAllStatsAndPushesAllParameters()
- throws Exception {
- setupFunctioningHardwareInterface();
- enableOffload();
-
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- // Pretend to set a few different upstreams (only the interface name
- // matters for this test; we're ignoring IP and route information).
- final LinkProperties upstreamLp = new LinkProperties();
- for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) {
- upstreamLp.setInterfaceName(ifname);
- offload.setUpstreamLinkProperties(upstreamLp);
- }
-
- // Pretend that some local prefixes and downstreams have been added
- // (and removed, for good measure).
- final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>();
- for (String s : new String[]{
- "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) {
- minimumLocalPrefixes.add(new IpPrefix(s));
- }
- offload.setLocalPrefixes(minimumLocalPrefixes);
-
- final LinkProperties usbLinkProperties = new LinkProperties();
- usbLinkProperties.setInterfaceName(RNDIS0);
- usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24"));
- usbLinkProperties.addRoute(
- new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST));
- offload.notifyDownstreamLinkProperties(usbLinkProperties);
-
- final LinkProperties wifiLinkProperties = new LinkProperties();
- wifiLinkProperties.setInterfaceName(WLAN0);
- wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24"));
- wifiLinkProperties.addRoute(
- new RouteInfo(new IpPrefix(WIFI_PREFIX), null, null, RTN_UNICAST));
- wifiLinkProperties.addRoute(
- new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST));
- // Use a benchmark prefix (RFC 5180 + erratum), since the documentation
- // prefix is included in the excluded prefix list.
- wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64"));
- wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64"));
- wifiLinkProperties.addRoute(
- new RouteInfo(new IpPrefix("2001:2::/64"), null, null, RTN_UNICAST));
- offload.notifyDownstreamLinkProperties(wifiLinkProperties);
-
- offload.removeDownstreamInterface(RNDIS0);
-
- // Clear invocation history, especially the getForwardedStats() calls
- // that happen with setUpstreamParameters().
- clearInvocations(mHardware);
-
- OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
- callback.onSupportAvailable();
-
- // Verify forwarded stats behaviour.
- verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
- verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
- mTetherStatsProviderCb.expectNotifyStatsUpdated();
- mTetherStatsProviderCb.assertNoCallback();
-
- // TODO: verify local prefixes and downstreams are also pushed to the HAL.
- verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
- ArrayList<String> localPrefixes = mStringArrayCaptor.getValue();
- assertEquals(4, localPrefixes.size());
- assertContainsAll(localPrefixes,
- // TODO: The logic to find and exclude downstream IP prefixes
- // is currently in Tethering's OffloadWrapper but must be moved
- // into OffloadController proper. After this, also check for:
- // "192.168.43.1/32", "2001:2::1/128", "2001:2::2/128"
- "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64");
- verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "192.168.43.0/24");
- verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "2001:2::/64");
- verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any());
- verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong());
- verifyNoMoreInteractions(mHardware);
- }
-
- @Test
- public void testOnSetAlert() throws Exception {
- setupFunctioningHardwareInterface();
- enableOffload();
- setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- final OffloadController offload = makeOffloadController();
- offload.start();
-
- // Initialize with fake eth upstream.
- final String ethernetIface = "eth1";
- InOrder inOrder = inOrder(mHardware);
- final LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
- // Previous upstream was null, so no stats are fetched.
- inOrder.verify(mHardware, never()).getForwardedStats(any());
-
- // Verify that set quota to 0 will immediately triggers an callback.
- mTetherStatsProvider.onSetAlert(0);
- waitForIdle();
- mTetherStatsProviderCb.expectNotifyAlertReached();
-
- // Verify that notifyAlertReached never fired if quota is not yet reached.
- when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
- new ForwardedStats(0, 0));
- mTetherStatsProvider.onSetAlert(100);
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- mTetherStatsProviderCb.assertNoCallback();
-
- // Verify that notifyAlertReached fired when quota is reached.
- when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
- new ForwardedStats(50, 50));
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- mTetherStatsProviderCb.expectNotifyAlertReached();
-
- // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch
- // any stats since the polling is stopped.
- reset(mHardware);
- mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED);
- mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- waitForIdle();
- mTetherStatsProviderCb.assertNoCallback();
- verify(mHardware, never()).getForwardedStats(any());
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
deleted file mode 100644
index 38b19dd3da5c..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ /dev/null
@@ -1,264 +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.networkstack.tethering;
-
-import static android.net.util.TetheringUtils.uint16;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_UNIX;
-import static android.system.OsConstants.SOCK_STREAM;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
-import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
-import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
-import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
-import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
-import android.net.netlink.StructNfGenMsg;
-import android.net.netlink.StructNlMsgHdr;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.NativeHandle;
-import android.os.test.TestLooper;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.FileDescriptor;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public final class OffloadHardwareInterfaceTest {
- private static final String RMNET0 = "test_rmnet_data0";
-
- private final TestLooper mTestLooper = new TestLooper();
-
- private OffloadHardwareInterface mOffloadHw;
- private ITetheringOffloadCallback mTetheringOffloadCallback;
- private OffloadHardwareInterface.ControlCallback mControlCallback;
-
- @Mock private IOffloadConfig mIOffloadConfig;
- @Mock private IOffloadControl mIOffloadControl;
- @Mock private NativeHandle mNativeHandle;
-
- // Random values to test Netlink message.
- private static final short TEST_TYPE = 184;
- private static final short TEST_FLAGS = 263;
-
- class MyDependencies extends OffloadHardwareInterface.Dependencies {
- MyDependencies(SharedLog log) {
- super(log);
- }
-
- @Override
- public IOffloadConfig getOffloadConfig() {
- return mIOffloadConfig;
- }
-
- @Override
- public IOffloadControl getOffloadControl() {
- return mIOffloadControl;
- }
-
- @Override
- public NativeHandle createConntrackSocket(final int groups) {
- return mNativeHandle;
- }
- }
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- final SharedLog log = new SharedLog("test");
- mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
- new MyDependencies(log));
- mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
- }
-
- private void startOffloadHardwareInterface() throws Exception {
- mOffloadHw.initOffloadConfig();
- mOffloadHw.initOffloadControl(mControlCallback);
- final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
- ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
- verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any());
- mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue();
- }
-
- @Test
- public void testGetForwardedStats() throws Exception {
- startOffloadHardwareInterface();
- final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
- verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
- assertNotNull(stats);
- }
-
- @Test
- public void testSetLocalPrefixes() throws Exception {
- startOffloadHardwareInterface();
- final ArrayList<String> localPrefixes = new ArrayList<>();
- localPrefixes.add("127.0.0.0/8");
- localPrefixes.add("fe80::/64");
- mOffloadHw.setLocalPrefixes(localPrefixes);
- verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any());
- }
-
- @Test
- public void testSetDataLimit() throws Exception {
- startOffloadHardwareInterface();
- final long limit = 12345;
- mOffloadHw.setDataLimit(RMNET0, limit);
- verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
- }
-
- @Test
- public void testSetUpstreamParameters() throws Exception {
- startOffloadHardwareInterface();
- final String v4addr = "192.168.10.1";
- final String v4gateway = "192.168.10.255";
- final ArrayList<String> v6gws = new ArrayList<>(0);
- v6gws.add("2001:db8::1");
- mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws);
- verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
- eq(v6gws), any());
-
- final ArgumentCaptor<ArrayList<String>> mArrayListCaptor =
- ArgumentCaptor.forClass(ArrayList.class);
- mOffloadHw.setUpstreamParameters(null, null, null, null);
- verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""),
- mArrayListCaptor.capture(), any());
- assertEquals(mArrayListCaptor.getValue().size(), 0);
- }
-
- @Test
- public void testUpdateDownstreamPrefix() throws Exception {
- startOffloadHardwareInterface();
- final String ifName = "wlan1";
- final String prefix = "192.168.43.0/24";
- mOffloadHw.addDownstreamPrefix(ifName, prefix);
- verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any());
-
- mOffloadHw.removeDownstreamPrefix(ifName, prefix);
- verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any());
- }
-
- @Test
- public void testTetheringOffloadCallback() throws Exception {
- startOffloadHardwareInterface();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onStarted();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onStoppedError();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onStoppedUnsupported();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onSupportAvailable();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onStoppedLimitReached();
-
- final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
- mTetheringOffloadCallback.updateTimeout(tcpParams);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
- eq(tcpParams.src.addr),
- eq(uint16(tcpParams.src.port)),
- eq(tcpParams.dst.addr),
- eq(uint16(tcpParams.dst.port)));
-
- final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
- mTetheringOffloadCallback.updateTimeout(udpParams);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
- eq(udpParams.src.addr),
- eq(uint16(udpParams.src.port)),
- eq(udpParams.dst.addr),
- eq(uint16(udpParams.dst.port)));
- }
-
- @Test
- public void testSendIpv4NfGenMsg() throws Exception {
- FileDescriptor writeSocket = new FileDescriptor();
- FileDescriptor readSocket = new FileDescriptor();
- try {
- Os.socketpair(AF_UNIX, SOCK_STREAM, 0, writeSocket, readSocket);
- } catch (ErrnoException e) {
- fail();
- return;
- }
- when(mNativeHandle.getFileDescriptor()).thenReturn(writeSocket);
-
- mOffloadHw.sendIpv4NfGenMsg(mNativeHandle, TEST_TYPE, TEST_FLAGS);
-
- ByteBuffer buffer = ByteBuffer.allocate(9823); // Arbitrary value > expectedLen.
- buffer.order(ByteOrder.nativeOrder());
-
- int read = Os.read(readSocket, buffer);
- final int expectedLen = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
- assertEquals(expectedLen, read);
-
- buffer.flip();
- assertEquals(expectedLen, buffer.getInt());
- assertEquals(TEST_TYPE, buffer.getShort());
- assertEquals(TEST_FLAGS, buffer.getShort());
- assertEquals(0 /* seq */, buffer.getInt());
- assertEquals(0 /* pid */, buffer.getInt());
- assertEquals(AF_INET, buffer.get()); // nfgen_family
- assertEquals(0 /* error */, buffer.get()); // version
- assertEquals(0 /* error */, buffer.getShort()); // res_id
- assertEquals(expectedLen, buffer.position());
- }
-
- private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
- final NatTimeoutUpdate params = new NatTimeoutUpdate();
- params.proto = proto;
- params.src.addr = "192.168.43.200";
- params.src.port = 100;
- params.dst.addr = "172.50.46.169";
- params.dst.port = 150;
- return params;
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
deleted file mode 100644
index 41d46e522ca4..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ /dev/null
@@ -1,551 +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.networkstack.tethering;
-
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.util.PrefixUtils.asIpPrefix;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.ip.IpServer;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public final class PrivateAddressCoordinatorTest {
- private static final String TEST_IFNAME = "test0";
-
- @Mock private IpServer mHotspotIpServer;
- @Mock private IpServer mUsbIpServer;
- @Mock private IpServer mEthernetIpServer;
- @Mock private IpServer mWifiP2pIpServer;
- @Mock private Context mContext;
- @Mock private ConnectivityManager mConnectivityMgr;
- @Mock private TetheringConfiguration mConfig;
-
- private PrivateAddressCoordinator mPrivateAddressCoordinator;
- private final LinkAddress mBluetoothAddress = new LinkAddress("192.168.44.1/24");
- private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24");
- private final Network mWifiNetwork = new Network(1);
- private final Network mMobileNetwork = new Network(2);
- private final Network mVpnNetwork = new Network(3);
- private final Network mMobileNetwork2 = new Network(4);
- private final Network mMobileNetwork3 = new Network(5);
- private final Network mMobileNetwork4 = new Network(6);
- private final Network mMobileNetwork5 = new Network(7);
- private final Network mMobileNetwork6 = new Network(8);
- private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork,
- mMobileNetwork2, mMobileNetwork3, mMobileNetwork4, mMobileNetwork5, mMobileNetwork6};
- private final ArrayList<IpPrefix> mTetheringPrefixes = new ArrayList<>(Arrays.asList(
- new IpPrefix("192.168.0.0/16"),
- new IpPrefix("172.16.0.0/12"),
- new IpPrefix("10.0.0.0/8")));
-
- private void setUpIpServers() throws Exception {
- when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
- when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET);
- when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI);
- when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
- when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
- when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
- when(mConfig.isSelectAllPrefixRangeEnabled()).thenReturn(true);
- setUpIpServers();
- mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
- }
-
- private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
- final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
- ipServer, useLastAddress);
- when(ipServer.getAddress()).thenReturn(address);
- return address;
- }
-
- @Test
- public void testRequestDownstreamAddressWithoutUsingLastAddress() throws Exception {
- final IpPrefix bluetoothPrefix = asIpPrefix(mBluetoothAddress);
- final LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
- final IpPrefix hotspotPrefix = asIpPrefix(address);
- assertNotEquals(hotspotPrefix, bluetoothPrefix);
-
- final LinkAddress newAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
- final IpPrefix testDupRequest = asIpPrefix(newAddress);
- assertNotEquals(hotspotPrefix, testDupRequest);
- assertNotEquals(bluetoothPrefix, testDupRequest);
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
-
- final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- false /* useLastAddress */);
- final IpPrefix usbPrefix = asIpPrefix(usbAddress);
- assertNotEquals(usbPrefix, bluetoothPrefix);
- assertNotEquals(usbPrefix, hotspotPrefix);
- mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
- }
-
- @Test
- public void testSanitizedAddress() throws Exception {
- int fakeSubAddr = 0x2b00; // 43.0.
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- LinkAddress actualAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
- assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress);
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
-
- fakeSubAddr = 0x2d01; // 45.1.
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
- assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress);
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
-
- fakeSubAddr = 0x2eff; // 46.255.
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
- assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress);
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
-
- fakeSubAddr = 0x2f05; // 47.5.
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
- assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress);
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
- }
-
- @Test
- public void testReservedPrefix() throws Exception {
- // - Test bluetooth prefix is reserved.
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
- getSubAddress(mBluetoothAddress.getAddress().getAddress()));
- final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
- final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress);
- assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix);
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
-
- // - Test previous enabled hotspot prefix(cached prefix) is reserved.
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
- getSubAddress(hotspotAddress.getAddress().getAddress()));
- final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- false /* useLastAddress */);
- final IpPrefix usbPrefix = asIpPrefix(usbAddress);
- assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix);
- assertNotEquals(hotspotPrefix, usbPrefix);
- mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
-
- // - Test wifi p2p prefix is reserved.
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
- getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
- final LinkAddress etherAddress = requestDownstreamAddress(mEthernetIpServer,
- false /* useLastAddress */);
- final IpPrefix etherPrefix = asIpPrefix(etherAddress);
- assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix);
- assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix);
- assertNotEquals(hotspotPrefix, etherPrefix);
- mPrivateAddressCoordinator.releaseDownstream(mEthernetIpServer);
- }
-
- @Test
- public void testRequestLastDownstreamAddress() throws Exception {
- final int fakeHotspotSubAddr = 0x2b05; // 43.5
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
- final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress);
-
- final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.45.5/24"), usbAddress);
-
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
- mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
-
- final int newFakeSubAddr = 0x3c05;
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
-
- final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals(hotspotAddress, newHotspotAddress);
- final LinkAddress newUsbAddress = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
- assertEquals(usbAddress, newUsbAddress);
-
- final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
- new LinkAddress("192.168.88.23/16"), null,
- makeNetworkCapabilities(TRANSPORT_WIFI));
- mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
- verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- verify(mUsbIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- }
-
- private UpstreamNetworkState buildUpstreamNetworkState(final Network network,
- final LinkAddress v4Addr, final LinkAddress v6Addr, final NetworkCapabilities cap) {
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(TEST_IFNAME);
- if (v4Addr != null) prop.addLinkAddress(v4Addr);
-
- if (v6Addr != null) prop.addLinkAddress(v6Addr);
-
- return new UpstreamNetworkState(prop, cap, network);
- }
-
- private NetworkCapabilities makeNetworkCapabilities(final int transportType) {
- final NetworkCapabilities cap = new NetworkCapabilities();
- cap.addTransportType(transportType);
- if (transportType == TRANSPORT_VPN) {
- cap.removeCapability(NET_CAPABILITY_NOT_VPN);
- }
-
- return cap;
- }
-
- @Test
- public void testNoConflictUpstreamPrefix() throws Exception {
- final int fakeHotspotSubAddr = 0x2b05; // 43.5
- final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
- // Force always get subAddress "43.5" for conflict testing.
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
- // - Enable hotspot with prefix 192.168.43.0/24
- final LinkAddress hotspotAddr = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddr);
- assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
- // - test mobile network with null NetworkCapabilities. Ideally this should not happen
- // because NetworkCapabilities update should always happen before LinkProperties update
- // and the UpstreamNetworkState update, just make sure no crash in this case.
- final UpstreamNetworkState noCapUpstream = buildUpstreamNetworkState(mMobileNetwork,
- new LinkAddress("10.0.0.8/24"), null, null);
- mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream);
- verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- // - test mobile upstream with no address.
- final UpstreamNetworkState noAddress = buildUpstreamNetworkState(mMobileNetwork,
- null, null, makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream);
- verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- // - Update v6 only mobile network, hotspot prefix should not be removed.
- final UpstreamNetworkState v6OnlyMobile = buildUpstreamNetworkState(mMobileNetwork,
- null, new LinkAddress("2001:db8::/64"),
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyMobile);
- verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork);
- // - Update v4 only mobile network, hotspot prefix should not be removed.
- final UpstreamNetworkState v4OnlyMobile = buildUpstreamNetworkState(mMobileNetwork,
- new LinkAddress("10.0.0.8/24"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyMobile);
- verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- // - Update v4v6 mobile network, hotspot prefix should not be removed.
- final UpstreamNetworkState v4v6Mobile = buildUpstreamNetworkState(mMobileNetwork,
- new LinkAddress("10.0.0.8/24"), new LinkAddress("2001:db8::/64"),
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(v4v6Mobile);
- verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- // - Update v6 only wifi network, hotspot prefix should not be removed.
- final UpstreamNetworkState v6OnlyWifi = buildUpstreamNetworkState(mWifiNetwork,
- null, new LinkAddress("2001:db8::/64"), makeNetworkCapabilities(TRANSPORT_WIFI));
- mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyWifi);
- verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
- // - Update vpn network, it conflict with hotspot prefix but VPN networks are ignored.
- final UpstreamNetworkState v4OnlyVpn = buildUpstreamNetworkState(mVpnNetwork,
- new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_VPN));
- mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyVpn);
- verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- // - Update v4 only wifi network, it conflict with hotspot prefix.
- final UpstreamNetworkState v4OnlyWifi = buildUpstreamNetworkState(mWifiNetwork,
- new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_WIFI));
- mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi);
- verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- reset(mHotspotIpServer);
- // - Restart hotspot again and its prefix is different previous.
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
- final LinkAddress hotspotAddr2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- final IpPrefix hotspotPrefix2 = asIpPrefix(hotspotAddr2);
- assertNotEquals(hotspotPrefix, hotspotPrefix2);
- mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi);
- verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- // - Usb tethering can be enabled and its prefix is different with conflict one.
- final LinkAddress usbAddr = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
- final IpPrefix usbPrefix = asIpPrefix(usbAddr);
- assertNotEquals(predefinedPrefix, usbPrefix);
- assertNotEquals(hotspotPrefix2, usbPrefix);
- // - Disable wifi upstream, then wifi's prefix can be selected again.
- mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
- final LinkAddress ethAddr = requestDownstreamAddress(mEthernetIpServer,
- true /* useLastAddress */);
- final IpPrefix ethPrefix = asIpPrefix(ethAddr);
- assertEquals(predefinedPrefix, ethPrefix);
- }
-
- @Test
- public void testChooseAvailablePrefix() throws Exception {
- final int randomAddress = 0x8605; // 134.5
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
- final LinkAddress addr0 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5.
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0);
- final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
- new LinkAddress("192.168.134.13/26"), null,
- makeNetworkCapabilities(TRANSPORT_WIFI));
- mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
-
- // Check whether return address is next prefix of 192.168.134.0/24.
- final LinkAddress addr1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1);
- final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork,
- new LinkAddress("192.168.149.16/19"), null,
- makeNetworkCapabilities(TRANSPORT_WIFI));
- mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream2);
-
-
- // The conflict range is 128 ~ 159, so the address is 192.168.160.5/24.
- final LinkAddress addr2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2);
- final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
- new LinkAddress("192.168.129.53/18"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- // Update another conflict upstream which is covered by the previous one (but not the first
- // one) and verify whether this would affect the result.
- final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
- new LinkAddress("192.168.170.7/19"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream);
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2);
-
- // The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24.
- final LinkAddress addr3 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3);
- final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
- new LinkAddress("192.168.188.133/17"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3);
-
- // Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because
- // 192.168.134/24 ~ 192.168.255.255/24 is not available.
- final LinkAddress addr4 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4);
- final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
- new LinkAddress("192.168.3.59/21"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4);
-
- // Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24.
- final LinkAddress addr5 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5);
- final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
- new LinkAddress("192.168.68.43/21"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5);
-
- // Update an upstream that does *not* conflict, check whether return the same address
- // 192.168.5/24.
- final LinkAddress addr6 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6);
- final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6,
- new LinkAddress("192.168.10.97/21"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream6);
-
- // Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24.
- final LinkAddress addr7 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7);
- final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6,
- new LinkAddress("192.168.0.0/17"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream7);
-
- // Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16.
- final LinkAddress addr8 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8);
- }
-
- @Test
- public void testChoosePrefixFromDifferentRanges() throws Exception {
- final int randomAddress = 0x1f2b2a; // 31.43.42
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
- final LinkAddress classC1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42.
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1);
- final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
- new LinkAddress("192.168.88.23/17"), null,
- makeNetworkCapabilities(TRANSPORT_WIFI));
- mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
- verifyNotifyConflictAndRelease(mHotspotIpServer);
-
- // Check whether return address is next address of prefix 192.168.128.0/17.
- final LinkAddress classC2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2);
- final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
- new LinkAddress("192.1.2.3/8"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream);
- verifyNotifyConflictAndRelease(mHotspotIpServer);
-
- // Check whether return address is under prefix 172.16.0.0/12.
- final LinkAddress classB1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1);
- final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
- new LinkAddress("172.28.123.100/14"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2);
- verifyNotifyConflictAndRelease(mHotspotIpServer);
-
- // 172.28.0.0 ~ 172.31.255.255 is not available.
- // Check whether return address is next address of prefix 172.16.0.0/14.
- final LinkAddress classB2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2);
-
- // Check whether new downstream is next address of address 172.16.0.42/24.
- final LinkAddress classB3 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3);
- final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
- new LinkAddress("172.16.0.1/24"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3);
- verifyNotifyConflictAndRelease(mHotspotIpServer);
- verify(mUsbIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
-
- // Check whether return address is next address of prefix 172.16.1.42/24.
- final LinkAddress classB4 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4);
- final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
- new LinkAddress("172.16.0.1/13"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4);
- verifyNotifyConflictAndRelease(mHotspotIpServer);
- verifyNotifyConflictAndRelease(mUsbIpServer);
-
- // Check whether return address is next address of prefix 172.16.0.1/13.
- final LinkAddress classB5 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5);
- // Check whether return address is next address of prefix 172.24.0.42/24.
- final LinkAddress classB6 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6);
- final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
- new LinkAddress("172.24.0.1/12"), null,
- makeNetworkCapabilities(TRANSPORT_CELLULAR));
- mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5);
- verifyNotifyConflictAndRelease(mHotspotIpServer);
- verifyNotifyConflictAndRelease(mUsbIpServer);
-
- // Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42.
- final LinkAddress classA1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1);
- // Check whether new downstream is next address of address 10.31.43.42/24.
- final LinkAddress classA2 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
- assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2);
- }
-
- private void verifyNotifyConflictAndRelease(final IpServer ipServer) throws Exception {
- verify(ipServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
- mPrivateAddressCoordinator.releaseDownstream(ipServer);
- reset(ipServer);
- setUpIpServers();
- }
-
- private int getSubAddress(final byte... ipv4Address) {
- assertEquals(4, ipv4Address.length);
-
- int subnet = Byte.toUnsignedInt(ipv4Address[2]);
- return (subnet << 8) + ipv4Address[3];
- }
-
- private void assertReseveredWifiP2pPrefix() throws Exception {
- LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
- final IpPrefix hotspotPrefix = asIpPrefix(address);
- final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress);
- assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix);
- mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
- }
-
- @Test
- public void testEnableLegacyWifiP2PAddress() throws Exception {
- when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
- getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
- // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix
- // is resevered.
- assertReseveredWifiP2pPrefix();
-
- when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true);
- assertReseveredWifiP2pPrefix();
-
- // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address.
- LinkAddress address = requestDownstreamAddress(mWifiP2pIpServer,
- true /* useLastAddress */);
- assertEquals(mLegacyWifiP2pAddress, address);
- mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer);
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
deleted file mode 100644
index 237e2c27bfa1..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.ConnectivityManager.TYPE_ETHERNET;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.util.SharedLog;
-import android.provider.DeviceConfig;
-import android.telephony.TelephonyManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.test.BroadcastInterceptingContext;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
-
-import java.util.Arrays;
-import java.util.Iterator;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TetheringConfigurationTest {
- private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
-
- private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
- private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
- private static final String PROVISIONING_APP_RESPONSE = "app_response";
- @Mock private Context mContext;
- @Mock private TelephonyManager mTelephonyManager;
- @Mock private Resources mResources;
- @Mock private Resources mResourcesForSubId;
- private Context mMockContext;
- private boolean mHasTelephonyManager;
- private boolean mEnableLegacyDhcpServer;
- private MockitoSession mMockingSession;
-
- private class MockTetheringConfiguration extends TetheringConfiguration {
- MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
- super(ctx, log, id);
- }
-
- @Override
- protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
- return mResourcesForSubId;
- }
- }
-
- private class MockContext extends BroadcastInterceptingContext {
- MockContext(Context base) {
- super(base);
- }
-
- @Override
- public Resources getResources() {
- return mResources;
- }
-
- @Override
- public Object getSystemService(String name) {
- if (Context.TELEPHONY_SERVICE.equals(name)) {
- return mHasTelephonyManager ? mTelephonyManager : null;
- }
- return super.getSystemService(name);
- }
- }
-
- @Before
- public void setUp() throws Exception {
- // TODO: use a dependencies class instead of mock statics.
- mMockingSession = mockitoSession()
- .initMocks(this)
- .mockStatic(DeviceConfig.class)
- .strictness(Strictness.WARN)
- .startMocking();
- doReturn(null).when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
-
- when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
- new String[0]);
- when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
- TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
- when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn(
- new String[0]);
- when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
- when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
- .thenReturn(new String[0]);
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
- false);
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
- .thenReturn(false);
- initializeBpfOffloadConfiguration(true, null /* unset */);
- initEnableSelectAllPrefixRangeFlag(null /* unset */);
-
- mHasTelephonyManager = true;
- mMockContext = new MockContext(mContext);
- mEnableLegacyDhcpServer = false;
- }
-
- @After
- public void tearDown() throws Exception {
- mMockingSession.finishMocking();
- }
-
- private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
- when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
- legacyTetherUpstreamTypes);
- return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- }
-
- @Test
- public void testNoTelephonyManagerMeansNoDun() {
- mHasTelephonyManager = false;
- final TetheringConfiguration cfg = getTetheringConfiguration(
- new int[]{TYPE_MOBILE_DUN, TYPE_WIFI});
- assertFalse(cfg.isDunRequired);
- assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
- // Just to prove we haven't clobbered Wi-Fi:
- assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
- }
-
- @Test
- public void testDunFromTelephonyManagerMeansDun() {
- when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true);
-
- final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
- final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
- TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI);
- final TetheringConfiguration cfgWifiDun = getTetheringConfiguration(
- TYPE_WIFI, TYPE_MOBILE_DUN);
- final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration(
- TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN);
-
- for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri,
- cfgWifiDun, cfgMobileWifiHipriDun)) {
- String msg = "config=" + cfg.toString();
- assertTrue(msg, cfg.isDunRequired);
- assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
- assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
- assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
- // Just to prove we haven't clobbered Wi-Fi:
- assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
- }
- }
-
- @Test
- public void testDunNotRequiredFromTelephonyManagerMeansNoDun() {
- when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
-
- final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
- final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
- TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI);
- final TetheringConfiguration cfgWifiDun = getTetheringConfiguration(
- TYPE_WIFI, TYPE_MOBILE_DUN);
- final TetheringConfiguration cfgWifiMobile = getTetheringConfiguration(
- TYPE_WIFI, TYPE_MOBILE);
- final TetheringConfiguration cfgWifiHipri = getTetheringConfiguration(
- TYPE_WIFI, TYPE_MOBILE_HIPRI);
- final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration(
- TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN);
-
- String msg;
- // TYPE_MOBILE_DUN should be present in none of the combinations.
- // TYPE_WIFI should not be affected.
- for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun,
- cfgWifiMobile, cfgWifiHipri, cfgMobileWifiHipriDun)) {
- msg = "config=" + cfg.toString();
- assertFalse(msg, cfg.isDunRequired);
- assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
- assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
- }
-
- for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun,
- cfgMobileWifiHipriDun)) {
- msg = "config=" + cfg.toString();
- assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
- assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
- }
- msg = "config=" + cfgWifiMobile.toString();
- assertTrue(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
- assertFalse(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
- msg = "config=" + cfgWifiHipri.toString();
- assertFalse(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
- assertTrue(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
-
- }
-
- @Test
- public void testNoDefinedUpstreamTypesAddsEthernet() {
- when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{});
- when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
-
- final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
- // The following is because the code always adds some kind of mobile
- // upstream, be it DUN or, in this case where DUN is NOT required,
- // make sure there is at least one of MOBILE or HIPRI. With the empty
- // list of the configuration in this test, it will always add both
- // MOBILE and HIPRI, in that order.
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_MOBILE, upstreamIterator.next().intValue());
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
- assertFalse(upstreamIterator.hasNext());
- }
-
- @Test
- public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
- when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
- new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
- when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
-
- final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
- assertFalse(upstreamIterator.hasNext());
- }
-
- @Test
- public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
- when(mResources.getIntArray(R.array.config_tether_upstream_types))
- .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
- when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
-
- final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
- assertTrue(upstreamIterator.hasNext());
- assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue());
- assertFalse(upstreamIterator.hasNext());
- }
-
- private void initializeBpfOffloadConfiguration(
- final boolean fromRes, final String fromDevConfig) {
- when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes);
- doReturn(fromDevConfig).when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD)));
- }
-
- @Test
- public void testBpfOffloadEnabledByResource() {
- initializeBpfOffloadConfiguration(true, null /* unset */);
- final TetheringConfiguration enableByRes =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(enableByRes.isBpfOffloadEnabled());
- }
-
- @Test
- public void testBpfOffloadEnabledByDeviceConfigOverride() {
- for (boolean res : new boolean[]{true, false}) {
- initializeBpfOffloadConfiguration(res, "true");
- final TetheringConfiguration enableByDevConOverride =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(enableByDevConOverride.isBpfOffloadEnabled());
- }
- }
-
- @Test
- public void testBpfOffloadDisabledByResource() {
- initializeBpfOffloadConfiguration(false, null /* unset */);
- final TetheringConfiguration disableByRes =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertFalse(disableByRes.isBpfOffloadEnabled());
- }
-
- @Test
- public void testBpfOffloadDisabledByDeviceConfigOverride() {
- for (boolean res : new boolean[]{true, false}) {
- initializeBpfOffloadConfiguration(res, "false");
- final TetheringConfiguration disableByDevConOverride =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertFalse(disableByDevConOverride.isBpfOffloadEnabled());
- }
- }
-
- @Test
- public void testNewDhcpServerDisabled() {
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
- true);
- doReturn("false").when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
-
- final TetheringConfiguration enableByRes =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(enableByRes.enableLegacyDhcpServer);
-
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
- false);
- doReturn("true").when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
-
- final TetheringConfiguration enableByDevConfig =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(enableByDevConfig.enableLegacyDhcpServer);
- }
-
- @Test
- public void testNewDhcpServerEnabled() {
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
- false);
- doReturn("false").when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
-
- final TetheringConfiguration cfg =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
-
- assertFalse(cfg.enableLegacyDhcpServer);
- }
-
- @Test
- public void testOffloadIntervalByResource() {
- final TetheringConfiguration intervalByDefault =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS,
- intervalByDefault.getOffloadPollInterval());
-
- final int[] testOverrides = {0, 3000, -1};
- for (final int override : testOverrides) {
- when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
- override);
- final TetheringConfiguration overrideByRes =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertEquals(override, overrideByRes.getOffloadPollInterval());
- }
- }
-
- @Test
- public void testGetResourcesBySubId() {
- setUpResourceForSubId();
- final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(cfg.provisioningApp.length == 0);
- final int anyValidSubId = 1;
- final MockTetheringConfiguration mockCfg =
- new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId);
- assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]);
- assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]);
- assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME);
- assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE);
- }
-
- private void setUpResourceForSubId() {
- when(mResourcesForSubId.getStringArray(
- R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
- when(mResourcesForSubId.getStringArray(
- R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
- when(mResourcesForSubId.getStringArray(
- R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
- when(mResourcesForSubId.getStringArray(
- R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]);
- when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
- new int[0]);
- when(mResourcesForSubId.getStringArray(
- R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
- when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
- .thenReturn(PROVISIONING_NO_UI_APP_NAME);
- when(mResourcesForSubId.getString(
- R.string.config_mobile_hotspot_provision_response)).thenReturn(
- PROVISIONING_APP_RESPONSE);
- }
-
- @Test
- public void testEnableLegacyWifiP2PAddress() throws Exception {
- final TetheringConfiguration defaultCfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp());
-
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
- .thenReturn(true);
- final TetheringConfiguration testCfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
- }
-
- private void initEnableSelectAllPrefixRangeFlag(final String value) {
- doReturn(value).when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES)));
- }
-
- @Test
- public void testSelectAllPrefixRangeFlag() throws Exception {
- // Test default value.
- final TetheringConfiguration defaultCfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(defaultCfg.isSelectAllPrefixRangeEnabled());
-
- // Test disable flag.
- initEnableSelectAllPrefixRangeFlag("false");
- final TetheringConfiguration testDisable = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertFalse(testDisable.isSelectAllPrefixRangeEnabled());
-
- // Test enable flag.
- initEnableSelectAllPrefixRangeFlag("true");
- final TetheringConfiguration testEnable = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(testEnable.isSelectAllPrefixRangeEnabled());
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
deleted file mode 100644
index 75c819bb0ced..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ /dev/null
@@ -1,444 +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.networkstack.tethering
-
-import android.app.Notification
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.app.PendingIntent.FLAG_IMMUTABLE
-import android.content.Context
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.content.pm.ResolveInfo
-import android.content.res.Resources
-import android.net.ConnectivityManager.TETHERING_WIFI
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
-import android.os.Handler
-import android.os.HandlerThread
-import android.os.Looper
-import android.os.UserHandle
-import android.provider.Settings
-import android.telephony.TelephonyManager
-import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.runner.AndroidJUnit4
-import com.android.internal.util.test.BroadcastInterceptingContext
-import com.android.networkstack.tethering.TetheringNotificationUpdater.ACTION_DISABLE_TETHERING
-import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
-import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
-import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
-import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
-import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID
-import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID
-import com.android.networkstack.tethering.TetheringNotificationUpdater.getSettingsPackageName
-import com.android.testutils.waitForIdle
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotNull
-import org.junit.Assert.fail
-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.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
-
-const val TEST_SUBID = 1
-const val WIFI_MASK = 1 shl TETHERING_WIFI
-const val TEST_DISALLOW_TITLE = "Tether function is disallowed"
-const val TEST_DISALLOW_MESSAGE = "Please contact your admin"
-const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access"
-const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet."
-const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot"
-const val TEST_ROAMING_TITLE = "Hotspot is on"
-const val TEST_ROAMING_MESSAGE = "Additional charges may apply while roaming."
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class TetheringNotificationUpdaterTest {
- // lateinit used here for mocks as they need to be reinitialized between each test and the test
- // should crash if they are used before being initialized.
- @Mock private lateinit var mockContext: Context
- @Mock private lateinit var notificationManager: NotificationManager
- @Mock private lateinit var telephonyManager: TelephonyManager
- @Mock private lateinit var testResources: Resources
-
- // lateinit for these classes under test, as they should be reset to a different instance for
- // every test but should always be initialized before use (or the test should crash).
- private lateinit var context: TestContext
- private lateinit var notificationUpdater: TetheringNotificationUpdater
-
- // Initializing the following members depends on initializing some of the mocks and
- // is more logically done in setup().
- private lateinit var fakeTetheringThread: HandlerThread
-
- private val ROAMING_CAPABILITIES = NetworkCapabilities()
- private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING)
- private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general
- private val TIMEOUT_MS = 500L
- private val ACTIVITY_PENDING_INTENT = 0
- private val BROADCAST_PENDING_INTENT = 1
-
- private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
- override fun createContextAsUser(user: UserHandle, flags: Int) =
- if (user == UserHandle.ALL) mockContext else this
- override fun getSystemService(name: String) =
- if (name == Context.TELEPHONY_SERVICE) telephonyManager
- else super.getSystemService(name)
- }
-
- private inner class WrappedNotificationUpdater(c: Context, looper: Looper)
- : TetheringNotificationUpdater(c, looper) {
- override fun getResourcesForSubId(c: Context, subId: Int) =
- if (subId == TEST_SUBID) testResources else super.getResourcesForSubId(c, subId)
- }
-
- private fun setupResources() {
- doReturn(5).`when`(testResources)
- .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
- doReturn(true).`when`(testResources)
- .getBoolean(R.bool.config_upstream_roaming_notification)
- doReturn(TEST_DISALLOW_TITLE).`when`(testResources)
- .getString(R.string.disable_tether_notification_title)
- doReturn(TEST_DISALLOW_MESSAGE).`when`(testResources)
- .getString(R.string.disable_tether_notification_message)
- doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources)
- .getString(R.string.no_upstream_notification_title)
- doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources)
- .getString(R.string.no_upstream_notification_message)
- doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources)
- .getString(R.string.no_upstream_notification_disable_button)
- doReturn(TEST_ROAMING_TITLE).`when`(testResources)
- .getString(R.string.upstream_roaming_notification_title)
- doReturn(TEST_ROAMING_MESSAGE).`when`(testResources)
- .getString(R.string.upstream_roaming_notification_message)
- }
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- context = TestContext(InstrumentationRegistry.getInstrumentation().context)
- doReturn(notificationManager).`when`(mockContext)
- .getSystemService(Context.NOTIFICATION_SERVICE)
- fakeTetheringThread = HandlerThread(this::class.java.simpleName)
- fakeTetheringThread.start()
- notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper)
- setupResources()
- }
-
- @After
- fun tearDown() {
- fakeTetheringThread.quitSafely()
- }
-
- private fun verifyActivityPendingIntent(intent: Intent, flags: Int) {
- // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add
- // the flag in creating arguments). If the described PendingIntent does not already exist,
- // getActivity() will return null instead of PendingIntent object.
- val pi = PendingIntent.getActivity(
- context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
- 0 /* requestCode */,
- intent,
- flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE,
- null /* options */)
- assertNotNull("Activity PendingIntent with FLAG_IMMUTABLE does not exist.", pi)
- }
-
- private fun verifyBroadcastPendingIntent(intent: Intent, flags: Int) {
- // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add
- // the flag in creating arguments). If the described PendingIntent does not already exist,
- // getBroadcast() will return null instead of PendingIntent object.
- val pi = PendingIntent.getBroadcast(
- context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
- 0 /* requestCode */,
- intent,
- flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE)
- assertNotNull("Broadcast PendingIntent with FLAG_IMMUTABLE does not exist.", pi)
- }
-
- private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
- private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
-
- private fun verifyNotification(
- iconId: Int,
- title: String,
- text: String,
- id: Int,
- intentSenderType: Int,
- intent: Intent,
- flags: Int
- ) {
- verify(notificationManager, never()).cancel(any(), eq(id))
-
- val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
- verify(notificationManager, times(1))
- .notify(any(), eq(id), notificationCaptor.capture())
-
- val notification = notificationCaptor.getValue()
- assertEquals(iconId, notification.smallIcon.resId)
- assertEquals(title, notification.title())
- assertEquals(text, notification.text())
-
- when (intentSenderType) {
- ACTIVITY_PENDING_INTENT -> verifyActivityPendingIntent(intent, flags)
- BROADCAST_PENDING_INTENT -> verifyBroadcastPendingIntent(intent, flags)
- }
-
- reset(notificationManager)
- }
-
- private fun verifyNotificationCancelled(
- notificationIds: List<Int>,
- resetAfterVerified: Boolean = true
- ) {
- notificationIds.forEach {
- verify(notificationManager, times(1)).cancel(any(), eq(it))
- }
- if (resetAfterVerified) reset(notificationManager)
- }
-
- @Test
- fun testRestrictedNotification() {
- val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS)
- .setPackage(getSettingsPackageName(context.packageManager))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
- // Set test sub id.
- notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
-
- // User restrictions on. Show restricted notification.
- notificationUpdater.notifyTetheringDisabledByRestriction()
- verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
- RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
-
- // User restrictions off. Clear notification.
- notificationUpdater.tetheringRestrictionLifted()
- verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID))
-
- // No downstream.
- notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyZeroInteractions(notificationManager)
-
- // User restrictions on again. Show restricted notification.
- notificationUpdater.notifyTetheringDisabledByRestriction()
- verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
- RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
- }
-
- val MAX_BACKOFF_MS = 200L
- /**
- * Waits for all messages, including delayed ones, to be processed.
- *
- * This will wait until the handler has no more messages to be processed including
- * delayed ones, or the timeout has expired. It uses an exponential backoff strategy
- * to wait longer and longer to consume less CPU, with the max granularity being
- * MAX_BACKOFF_MS.
- *
- * @return true if all messages have been processed including delayed ones, false if timeout
- *
- * TODO: Move this method to com.android.testutils.HandlerUtils.kt.
- */
- private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) {
- fun hasMatchingMessages() =
- if (what == null) hasMessagesOrCallbacks() else hasMessages(what)
- val expiry = System.currentTimeMillis() + timeoutMs
- var delay = 5L
- while (System.currentTimeMillis() < expiry && hasMatchingMessages()) {
- // None of Handler, Looper, Message and MessageQueue expose any way to retrieve
- // the time when the next (let alone the last) message will be processed, so
- // short of examining the internals with reflection sleep() is the only solution.
- Thread.sleep(delay)
- delay = (delay * 2)
- .coerceAtMost(expiry - System.currentTimeMillis())
- .coerceAtMost(MAX_BACKOFF_MS)
- }
-
- val timeout = expiry - System.currentTimeMillis()
- if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms")
- waitForIdle(timeout)
- }
-
- @Test
- fun testNoUpstreamNotification() {
- val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName)
-
- // Set test sub id.
- notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
-
- // Wifi downstream.
- notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
-
- // There is no upstream. Show no upstream notification.
- notificationUpdater.onUpstreamCapabilitiesChanged(null)
- notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
- verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
- NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
- FLAG_IMMUTABLE)
-
- // Same capabilities changed. Nothing happened.
- notificationUpdater.onUpstreamCapabilitiesChanged(null)
- verifyZeroInteractions(notificationManager)
-
- // Upstream come back. Clear no upstream notification.
- notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID))
-
- // No upstream again. Show no upstream notification.
- notificationUpdater.onUpstreamCapabilitiesChanged(null)
- notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
- verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
- NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
- FLAG_IMMUTABLE)
-
- // No downstream.
- notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
-
- // Wifi downstream and home capabilities.
- notificationUpdater.onDownstreamChanged(WIFI_MASK)
- notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
-
- // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream
- // again. Don't put up no upstream notification.
- doReturn(-1).`when`(testResources)
- .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
- notificationUpdater.onUpstreamCapabilitiesChanged(null)
- notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID))
- }
-
- @Test
- fun testGetResourcesForSubId() {
- doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt())
- doReturn(1234).`when`(telephonyManager).getSimCarrierId()
- doReturn("000000").`when`(telephonyManager).getSimOperator()
-
- val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId.
- val config = context.resources.configuration
- var res = notificationUpdater.getResourcesForSubId(context, subId)
- assertEquals(config.mcc, res.configuration.mcc)
- assertEquals(config.mnc, res.configuration.mnc)
-
- doReturn(VERIZON_CARRIER_ID).`when`(telephonyManager).getSimCarrierId()
- res = notificationUpdater.getResourcesForSubId(context, subId)
- assertEquals(config.mcc, res.configuration.mcc)
- assertEquals(config.mnc, res.configuration.mnc)
-
- doReturn("20404").`when`(telephonyManager).getSimOperator()
- res = notificationUpdater.getResourcesForSubId(context, subId)
- assertEquals(311, res.configuration.mcc)
- assertEquals(480, res.configuration.mnc)
- }
-
- @Test
- fun testRoamingNotification() {
- val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName)
- val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS)
- .setPackage(getSettingsPackageName(context.packageManager))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
- // Set test sub id.
- notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
-
- // Wifi downstream.
- notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
-
- // Upstream capabilities changed to roaming state. Show roaming notification.
- notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
- verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
- ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
-
- // Same capabilities change. Nothing happened.
- notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
- verifyZeroInteractions(notificationManager)
-
- // Upstream capabilities changed to home state. Clear roaming notification.
- notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
- verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID))
-
- // Upstream capabilities changed to roaming state again. Show roaming notification.
- notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
- verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
- ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
-
- // No upstream. Clear roaming notification and show no upstream notification.
- notificationUpdater.onUpstreamCapabilitiesChanged(null)
- notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
- verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
- verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
- NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
- FLAG_IMMUTABLE)
-
- // No downstream.
- notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
-
- // Wifi downstream again.
- notificationUpdater.onDownstreamChanged(WIFI_MASK)
- notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
- verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
- verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
- NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
- FLAG_IMMUTABLE)
-
- // Set R.bool.config_upstream_roaming_notification to false and change upstream
- // network to roaming state again. No roaming notification.
- doReturn(false).`when`(testResources)
- .getBoolean(R.bool.config_upstream_roaming_notification)
- notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
- verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
- }
-
- @Test
- fun testGetSettingsPackageName() {
- val defaultSettingsPackageName = "com.android.settings"
- val testSettingsPackageName = "com.android.test.settings"
- val pm = mock(PackageManager::class.java)
- doReturn(null).`when`(pm).resolveActivity(any(), anyInt())
- assertEquals(defaultSettingsPackageName, getSettingsPackageName(pm))
-
- val resolveInfo = ResolveInfo().apply {
- activityInfo = ActivityInfo().apply {
- name = "test"
- applicationInfo = ApplicationInfo().apply {
- packageName = testSettingsPackageName
- }
- }
- }
- doReturn(resolveInfo).`when`(pm).resolveActivity(any(), anyInt())
- assertEquals(testSettingsPackageName, getSettingsPackageName(pm))
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
deleted file mode 100644
index 7bba67b05f88..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ /dev/null
@@ -1,484 +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.networkstack.tethering;
-
-import static android.Manifest.permission.ACCESS_NETWORK_STATE;
-import static android.Manifest.permission.TETHER_PRIVILEGED;
-import static android.Manifest.permission.WRITE_SETTINGS;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
-import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.UiAutomation;
-import android.content.Intent;
-import android.net.IIntResultListener;
-import android.net.ITetheringConnector;
-import android.net.ITetheringEventCallback;
-import android.net.TetheringRequestParcel;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.rule.ServiceTestRule;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.networkstack.tethering.MockTetheringService.MockTetheringConnector;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public final class TetheringServiceTest {
- private static final String TEST_IFACE_NAME = "test_wlan0";
- private static final String TEST_CALLER_PKG = "com.android.shell";
- private static final String TEST_ATTRIBUTION_TAG = null;
- @Mock private ITetheringEventCallback mITetheringEventCallback;
- @Rule public ServiceTestRule mServiceTestRule;
- private Tethering mTethering;
- private Intent mMockServiceIntent;
- private ITetheringConnector mTetheringConnector;
- private UiAutomation mUiAutomation;
-
- private class TestTetheringResult extends IIntResultListener.Stub {
- private int mResult = -1; // Default value that does not match any result code.
- @Override
- public void onResult(final int resultCode) {
- mResult = resultCode;
- }
-
- public void assertResult(final int expected) {
- assertEquals(expected, mResult);
- }
- }
-
- private class MyResultReceiver extends ResultReceiver {
- MyResultReceiver(Handler handler) {
- super(handler);
- }
- private int mResult = -1; // Default value that does not match any result code.
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- mResult = resultCode;
- }
-
- public void assertResult(int expected) {
- assertEquals(expected, mResult);
- }
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mUiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
- mServiceTestRule = new ServiceTestRule();
- mMockServiceIntent = new Intent(
- InstrumentationRegistry.getTargetContext(),
- MockTetheringService.class);
- final MockTetheringConnector mockConnector =
- (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent);
- mTetheringConnector = mockConnector.getTetheringConnector();
- final MockTetheringService service = mockConnector.getService();
- mTethering = service.getTethering();
- }
-
- @After
- public void tearDown() throws Exception {
- mServiceTestRule.unbindService();
- mUiAutomation.dropShellPermissionIdentity();
- }
-
- private interface TestTetheringCall {
- void runTetheringCall(TestTetheringResult result) throws Exception;
- }
-
- private void runAsNoPermission(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, new String[0]);
- }
-
- private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, TETHER_PRIVILEGED);
- }
-
- private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, ACCESS_NETWORK_STATE);
- }
-
- private void runAsWriteSettings(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, WRITE_SETTINGS);
- }
-
- private void runTetheringCall(final TestTetheringCall test, String... permissions)
- throws Exception {
- if (permissions.length > 0) mUiAutomation.adoptShellPermissionIdentity(permissions);
- try {
- when(mTethering.isTetheringSupported()).thenReturn(true);
- test.runTetheringCall(new TestTetheringResult());
- } finally {
- mUiAutomation.dropShellPermissionIdentity();
- }
- }
-
- private void verifyNoMoreInteractionsForTethering() {
- verifyNoMoreInteractions(mTethering);
- verifyNoMoreInteractions(mITetheringEventCallback);
- reset(mTethering, mITetheringEventCallback);
- }
-
- private void runTether(final TestTetheringResult result) throws Exception {
- when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
- mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringSupported();
- verify(mTethering).tether(TEST_IFACE_NAME);
- result.assertResult(TETHER_ERROR_NO_ERROR);
- }
-
- @Test
- public void testTether() throws Exception {
- runAsNoPermission((result) -> {
- mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering).isTetherProvisioningRequired();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((result) -> {
- runTether(result);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsWriteSettings((result) -> {
- runTether(result);
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
- });
- }
-
- private void runUnTether(final TestTetheringResult result) throws Exception {
- when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
- mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering).isTetheringSupported();
- verify(mTethering).untether(TEST_IFACE_NAME);
- result.assertResult(TETHER_ERROR_NO_ERROR);
- }
-
- @Test
- public void testUntether() throws Exception {
- runAsNoPermission((result) -> {
- mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering).isTetherProvisioningRequired();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((result) -> {
- runUnTether(result);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsWriteSettings((result) -> {
- runUnTether(result);
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
- });
- }
-
- private void runSetUsbTethering(final TestTetheringResult result) throws Exception {
- when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR);
- mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
- TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringSupported();
- verify(mTethering).setUsbTethering(true /* enable */);
- result.assertResult(TETHER_ERROR_NO_ERROR);
- }
-
- @Test
- public void testSetUsbTethering() throws Exception {
- runAsNoPermission((result) -> {
- mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
- TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetherProvisioningRequired();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((result) -> {
- runSetUsbTethering(result);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsWriteSettings((result) -> {
- runSetUsbTethering(result);
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
- });
-
- }
-
- private void runStartTethering(final TestTetheringResult result,
- final TetheringRequestParcel request) throws Exception {
- mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering).isTetheringSupported();
- verify(mTethering).startTethering(eq(request), eq(result));
- }
-
- @Test
- public void testStartTethering() throws Exception {
- final TetheringRequestParcel request = new TetheringRequestParcel();
- request.tetheringType = TETHERING_WIFI;
-
- runAsNoPermission((result) -> {
- mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering).isTetherProvisioningRequired();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((result) -> {
- runStartTethering(result, request);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsWriteSettings((result) -> {
- runStartTethering(result, request);
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
- });
- }
-
- private void runStartTetheringAndVerifyNoPermission(final TestTetheringResult result)
- throws Exception {
- final TetheringRequestParcel request = new TetheringRequestParcel();
- request.tetheringType = TETHERING_WIFI;
- request.exemptFromEntitlementCheck = true;
- mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- }
-
- @Test
- public void testFailToBypassEntitlementWithoutNeworkStackPermission() throws Exception {
- final TetheringRequestParcel request = new TetheringRequestParcel();
- request.tetheringType = TETHERING_WIFI;
- request.exemptFromEntitlementCheck = true;
-
- runAsNoPermission((result) -> {
- runStartTetheringAndVerifyNoPermission(result);
- });
-
- runAsTetherPrivileged((result) -> {
- runStartTetheringAndVerifyNoPermission(result);
- });
-
- runAsWriteSettings((result) -> {
- runStartTetheringAndVerifyNoPermission(result);
- });
- }
-
- private void runStopTethering(final TestTetheringResult result) throws Exception {
- mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
- TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringSupported();
- verify(mTethering).stopTethering(TETHERING_WIFI);
- result.assertResult(TETHER_ERROR_NO_ERROR);
- }
-
- @Test
- public void testStopTethering() throws Exception {
- runAsNoPermission((result) -> {
- mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
- TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetherProvisioningRequired();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((result) -> {
- runStopTethering(result);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsWriteSettings((result) -> {
- runStopTethering(result);
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
- });
- }
-
- private void runRequestLatestTetheringEntitlementResult() throws Exception {
- final MyResultReceiver result = new MyResultReceiver(null);
- mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
- true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
- verify(mTethering).isTetheringSupported();
- verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI),
- eq(result), eq(true) /* showEntitlementUi */);
- }
-
- @Test
- public void testRequestLatestTetheringEntitlementResult() throws Exception {
- // Run as no permission.
- final MyResultReceiver result = new MyResultReceiver(null);
- mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
- true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
- verify(mTethering).isTetherProvisioningRequired();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractions(mTethering);
-
- runAsTetherPrivileged((none) -> {
- runRequestLatestTetheringEntitlementResult();
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsWriteSettings((none) -> {
- runRequestLatestTetheringEntitlementResult();
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
- });
- }
-
- private void runRegisterTetheringEventCallback() throws Exception {
- mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
- TEST_CALLER_PKG);
- verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback));
- }
-
- @Test
- public void testRegisterTetheringEventCallback() throws Exception {
- runAsNoPermission((result) -> {
- mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
- TEST_CALLER_PKG);
- verify(mITetheringEventCallback).onCallbackStopped(
- TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((none) -> {
- runRegisterTetheringEventCallback();
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsAccessNetworkState((none) -> {
- runRegisterTetheringEventCallback();
- verifyNoMoreInteractionsForTethering();
- });
- }
-
- private void runUnregisterTetheringEventCallback() throws Exception {
- mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
- TEST_CALLER_PKG);
- verify(mTethering).unregisterTetheringEventCallback(eq(mITetheringEventCallback));
- }
-
- @Test
- public void testUnregisterTetheringEventCallback() throws Exception {
- runAsNoPermission((result) -> {
- mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
- TEST_CALLER_PKG);
- verify(mITetheringEventCallback).onCallbackStopped(
- TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((none) -> {
- runUnregisterTetheringEventCallback();
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsAccessNetworkState((none) -> {
- runUnregisterTetheringEventCallback();
- verifyNoMoreInteractionsForTethering();
- });
- }
-
- private void runStopAllTethering(final TestTetheringResult result) throws Exception {
- mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringSupported();
- verify(mTethering).untetherAll();
- result.assertResult(TETHER_ERROR_NO_ERROR);
- }
-
- @Test
- public void testStopAllTethering() throws Exception {
- runAsNoPermission((result) -> {
- mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetherProvisioningRequired();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((result) -> {
- runStopAllTethering(result);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsWriteSettings((result) -> {
- runStopAllTethering(result);
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
- });
- }
-
- private void runIsTetheringSupported(final TestTetheringResult result) throws Exception {
- mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringSupported();
- result.assertResult(TETHER_ERROR_NO_ERROR);
- }
-
- @Test
- public void testIsTetheringSupported() throws Exception {
- runAsNoPermission((result) -> {
- mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering).isTetherProvisioningRequired();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsTetherPrivileged((result) -> {
- runIsTetheringSupported(result);
- verifyNoMoreInteractionsForTethering();
- });
-
- runAsWriteSettings((result) -> {
- runIsTetheringSupported(result);
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
- });
- }
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
deleted file mode 100644
index 114cb7ca6ec7..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ /dev/null
@@ -1,2019 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.content.pm.PackageManager.GET_ACTIVITIES;
-import static android.hardware.usb.UsbManager.USB_CONFIGURED;
-import static android.hardware.usb.UsbManager.USB_CONNECTED;
-import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM;
-import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
-import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
-import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
-import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
-import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
-import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringManager.TETHERING_NCM;
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
-import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
-import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
-import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
-import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
-import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.usage.NetworkStatsManager;
-import android.bluetooth.BluetoothAdapter;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
-import android.net.EthernetManager;
-import android.net.EthernetManager.TetheredInterfaceCallback;
-import android.net.EthernetManager.TetheredInterfaceRequest;
-import android.net.IIntResultListener;
-import android.net.INetd;
-import android.net.ITetheringEventCallback;
-import android.net.InetAddresses;
-import android.net.InterfaceConfigurationParcel;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.MacAddress;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.RouteInfo;
-import android.net.TetherStatesParcel;
-import android.net.TetheredClient;
-import android.net.TetheringCallbackStartedParcel;
-import android.net.TetheringConfigurationParcel;
-import android.net.TetheringRequestParcel;
-import android.net.dhcp.DhcpServerCallbacks;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpServer;
-import android.net.ip.DadProxy;
-import android.net.ip.IpNeighborMonitor;
-import android.net.ip.IpServer;
-import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.InterfaceParams;
-import android.net.util.NetworkConstants;
-import android.net.util.SharedLog;
-import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiManager;
-import android.net.wifi.p2p.WifiP2pGroup;
-import android.net.wifi.p2p.WifiP2pInfo;
-import android.net.wifi.p2p.WifiP2pManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.os.test.TestLooper;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
-import android.test.mock.MockContentResolver;
-
-import androidx.annotation.NonNull;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.testutils.MiscAsserts;
-
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Vector;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TetheringTest {
- private static final int IFINDEX_OFFSET = 100;
-
- private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
- private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
- private static final String TEST_USB_IFNAME = "test_rndis0";
- private static final String TEST_WIFI_IFNAME = "test_wlan0";
- private static final String TEST_WLAN_IFNAME = "test_wlan1";
- private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
- private static final String TEST_NCM_IFNAME = "test_ncm0";
- private static final String TEST_ETH_IFNAME = "test_eth0";
- private static final String TEST_BT_IFNAME = "test_pan0";
- private static final String TETHERING_NAME = "Tethering";
- private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
- private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
-
- private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
-
- @Mock private ApplicationInfo mApplicationInfo;
- @Mock private Context mContext;
- @Mock private NetworkStatsManager mStatsManager;
- @Mock private OffloadHardwareInterface mOffloadHardwareInterface;
- @Mock private OffloadHardwareInterface.ForwardedStats mForwardedStats;
- @Mock private Resources mResources;
- @Mock private TelephonyManager mTelephonyManager;
- @Mock private UsbManager mUsbManager;
- @Mock private WifiManager mWifiManager;
- @Mock private CarrierConfigManager mCarrierConfigManager;
- @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
- @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
- @Mock private DadProxy mDadProxy;
- @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
- @Mock private IpNeighborMonitor mIpNeighborMonitor;
- @Mock private IDhcpServer mDhcpServer;
- @Mock private INetd mNetd;
- @Mock private UserManager mUserManager;
- @Mock private NetworkRequest mNetworkRequest;
- @Mock private ConnectivityManager mCm;
- @Mock private EthernetManager mEm;
- @Mock private TetheringNotificationUpdater mNotificationUpdater;
- @Mock private BpfCoordinator mBpfCoordinator;
- @Mock private PackageManager mPackageManager;
-
- private final MockIpServerDependencies mIpServerDependencies =
- spy(new MockIpServerDependencies());
- private final MockTetheringDependencies mTetheringDependencies =
- new MockTetheringDependencies();
-
- // Like so many Android system APIs, these cannot be mocked because it is marked final.
- // We have to use the real versions.
- private final PersistableBundle mCarrierConfig = new PersistableBundle();
- private final TestLooper mLooper = new TestLooper();
-
- private Vector<Intent> mIntents;
- private BroadcastInterceptingContext mServiceContext;
- private MockContentResolver mContentResolver;
- private BroadcastReceiver mBroadcastReceiver;
- private Tethering mTethering;
- private PhoneStateListener mPhoneStateListener;
- private InterfaceConfigurationParcel mInterfaceConfiguration;
- private TetheringConfiguration mConfig;
- private EntitlementManager mEntitleMgr;
- private OffloadController mOffloadCtrl;
- private PrivateAddressCoordinator mPrivateAddressCoordinator;
-
- private class TestContext extends BroadcastInterceptingContext {
- TestContext(Context base) {
- super(base);
- }
-
- @Override
- public ApplicationInfo getApplicationInfo() {
- return mApplicationInfo;
- }
-
- @Override
- public ContentResolver getContentResolver() {
- return mContentResolver;
- }
-
- @Override
- public String getPackageName() {
- return "TetheringTest";
- }
-
- @Override
- public Resources getResources() {
- return mResources;
- }
-
- @Override
- public Object getSystemService(String name) {
- if (Context.WIFI_SERVICE.equals(name)) return mWifiManager;
- if (Context.USB_SERVICE.equals(name)) return mUsbManager;
- if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
- if (Context.USER_SERVICE.equals(name)) return mUserManager;
- if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
- if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
- if (Context.ETHERNET_SERVICE.equals(name)) return mEm;
- return super.getSystemService(name);
- }
-
- @Override
- public PackageManager getPackageManager() {
- return mPackageManager;
- }
-
- @Override
- public String getSystemServiceName(Class<?> serviceClass) {
- if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE;
- return super.getSystemServiceName(serviceClass);
- }
- }
-
- public class MockIpServerDependencies extends IpServer.Dependencies {
- @Override
- public DadProxy getDadProxy(
- Handler handler, InterfaceParams ifParams) {
- return mDadProxy;
- }
-
- @Override
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
- InterfaceParams ifParams) {
- return mRouterAdvertisementDaemon;
- }
-
- @Override
- public InterfaceParams getInterfaceParams(String ifName) {
- assertTrue("Non-mocked interface " + ifName,
- ifName.equals(TEST_USB_IFNAME)
- || ifName.equals(TEST_WLAN_IFNAME)
- || ifName.equals(TEST_MOBILE_IFNAME)
- || ifName.equals(TEST_P2P_IFNAME)
- || ifName.equals(TEST_NCM_IFNAME)
- || ifName.equals(TEST_ETH_IFNAME));
- final String[] ifaces = new String[] {
- TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME,
- TEST_NCM_IFNAME, TEST_ETH_IFNAME};
- return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
- MacAddress.ALL_ZEROS_ADDRESS);
- }
-
- @Override
- public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
- DhcpServerCallbacks cb) {
- new Thread(() -> {
- try {
- cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
- }).run();
- }
-
- public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l,
- IpNeighborMonitor.NeighborEventConsumer c) {
- return mIpNeighborMonitor;
- }
- }
-
- // MyTetheringConfiguration is used to override static method for testing.
- private class MyTetheringConfiguration extends TetheringConfiguration {
- MyTetheringConfiguration(Context ctx, SharedLog log, int id) {
- super(ctx, log, id);
- }
-
- @Override
- protected String getDeviceConfigProperty(final String name) {
- return null;
- }
-
- @Override
- protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
- return mResources;
- }
- }
-
- public class MockTetheringDependencies extends TetheringDependencies {
- StateMachine mUpstreamNetworkMonitorSM;
- ArrayList<IpServer> mIpv6CoordinatorNotifyList;
-
- public void reset() {
- mUpstreamNetworkMonitorSM = null;
- mIpv6CoordinatorNotifyList = null;
- }
-
- @Override
- public BpfCoordinator getBpfCoordinator(
- BpfCoordinator.Dependencies deps) {
- return mBpfCoordinator;
- }
-
- @Override
- public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
- return mOffloadHardwareInterface;
- }
-
- @Override
- public OffloadController getOffloadController(Handler h, SharedLog log,
- OffloadController.Dependencies deps) {
- mOffloadCtrl = spy(super.getOffloadController(h, log, deps));
- // Return real object here instead of mock because
- // testReportFailCallbackIfOffloadNotSupported depend on real OffloadController object.
- return mOffloadCtrl;
- }
-
- @Override
- public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
- StateMachine target, SharedLog log, int what) {
- mUpstreamNetworkMonitorSM = target;
- return mUpstreamNetworkMonitor;
- }
-
- @Override
- public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
- ArrayList<IpServer> notifyList, SharedLog log) {
- mIpv6CoordinatorNotifyList = notifyList;
- return mIPv6TetheringCoordinator;
- }
-
- @Override
- public IpServer.Dependencies getIpServerDependencies() {
- return mIpServerDependencies;
- }
-
- @Override
- public NetworkRequest getDefaultNetworkRequest() {
- return mNetworkRequest;
- }
-
- @Override
- public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
- Runnable callback) {
- mEntitleMgr = spy(super.getEntitlementManager(ctx, h, log, callback));
- return mEntitleMgr;
- }
-
- @Override
- public boolean isTetheringSupported() {
- return true;
- }
-
- @Override
- public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
- int subId) {
- mConfig = spy(new MyTetheringConfiguration(ctx, log, subId));
- return mConfig;
- }
-
- @Override
- public INetd getINetd(Context context) {
- return mNetd;
- }
-
- @Override
- public Looper getTetheringLooper() {
- return mLooper.getLooper();
- }
-
- @Override
- public Context getContext() {
- return mServiceContext;
- }
-
- @Override
- public BluetoothAdapter getBluetoothAdapter() {
- // TODO: add test for bluetooth tethering.
- return null;
- }
-
- @Override
- public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) {
- return mNotificationUpdater;
- }
-
- @Override
- public boolean isTetheringDenied() {
- return false;
- }
-
-
- @Override
- public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
- TetheringConfiguration cfg) {
- mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg);
- return mPrivateAddressCoordinator;
- }
- }
-
- private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
- boolean withIPv6, boolean with464xlat) {
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(TEST_MOBILE_IFNAME);
-
- if (withIPv4) {
- prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
- InetAddresses.parseNumericAddress("10.0.0.1"),
- TEST_MOBILE_IFNAME, RTN_UNICAST));
- }
-
- if (withIPv6) {
- prop.addDnsServer(InetAddresses.parseNumericAddress("2001:db8::2"));
- prop.addLinkAddress(
- new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
- NetworkConstants.RFC7421_PREFIX_LENGTH));
- prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
- InetAddresses.parseNumericAddress("2001:db8::1"),
- TEST_MOBILE_IFNAME, RTN_UNICAST));
- }
-
- if (with464xlat) {
- final LinkProperties stackedLink = new LinkProperties();
- stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
- stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
- InetAddresses.parseNumericAddress("192.0.0.1"),
- TEST_XLAT_MOBILE_IFNAME, RTN_UNICAST));
-
- prop.addStackedLink(stackedLink);
- }
-
-
- final NetworkCapabilities capabilities = new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- return new UpstreamNetworkState(prop, capabilities, new Network(100));
- }
-
- private static UpstreamNetworkState buildMobileIPv4UpstreamState() {
- return buildMobileUpstreamState(true, false, false);
- }
-
- private static UpstreamNetworkState buildMobileIPv6UpstreamState() {
- return buildMobileUpstreamState(false, true, false);
- }
-
- private static UpstreamNetworkState buildMobileDualStackUpstreamState() {
- return buildMobileUpstreamState(true, true, false);
- }
-
- private static UpstreamNetworkState buildMobile464xlatUpstreamState() {
- return buildMobileUpstreamState(false, true, true);
- }
-
- // See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and
- // after use.
- @BeforeClass
- public static void setupOnce() {
- FakeSettingsProvider.clearSettingsProvider();
- }
-
- @AfterClass
- public static void tearDownOnce() {
- FakeSettingsProvider.clearSettingsProvider();
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- when(mResources.getStringArray(R.array.config_tether_dhcp_range))
- .thenReturn(new String[0]);
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
- false);
- when(mNetd.interfaceGetList())
- .thenReturn(new String[] {
- TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
- TEST_NCM_IFNAME, TEST_ETH_IFNAME});
- when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
- mInterfaceConfiguration = new InterfaceConfigurationParcel();
- mInterfaceConfiguration.flags = new String[0];
- when(mRouterAdvertisementDaemon.start())
- .thenReturn(true);
- initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */,
- 0 /* defaultDisabled */);
- when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats);
-
- mServiceContext = new TestContext(mContext);
- mContentResolver = new MockContentResolver(mServiceContext);
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- setTetheringSupported(true /* supported */);
- mIntents = new Vector<>();
- mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mIntents.addElement(intent);
- }
- };
- mServiceContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(ACTION_TETHER_STATE_CHANGED));
- mTethering = makeTethering();
- verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
- verify(mNetd).registerUnsolicitedEventListener(any());
- final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
- ArgumentCaptor.forClass(PhoneStateListener.class);
- verify(mTelephonyManager).listen(phoneListenerCaptor.capture(),
- eq(PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE));
- verify(mWifiManager).registerSoftApCallback(any(), any());
- mPhoneStateListener = phoneListenerCaptor.getValue();
- }
-
- private void setTetheringSupported(final boolean supported) {
- Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED,
- supported ? 1 : 0);
- when(mUserManager.hasUserRestriction(
- UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported);
- // Setup tetherable configuration.
- when(mResources.getStringArray(R.array.config_tether_usb_regexs))
- .thenReturn(new String[] { "test_rndis\\d" });
- when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
- .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
- when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
- when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
- .thenReturn(new String[] { "test_ncm\\d" });
- when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
- when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
- }
-
- private void initTetheringUpstream(UpstreamNetworkState upstreamState) {
- when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
- when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
- }
-
- private Tethering makeTethering() {
- mTetheringDependencies.reset();
- return new Tethering(mTetheringDependencies);
- }
-
- private TetheringRequestParcel createTetheringRequestParcel(final int type) {
- return createTetheringRequestParcel(type, null, null, false);
- }
-
- private TetheringRequestParcel createTetheringRequestParcel(final int type,
- final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt) {
- final TetheringRequestParcel request = new TetheringRequestParcel();
- request.tetheringType = type;
- request.localIPv4Address = serverAddr;
- request.staticClientAddress = clientAddr;
- request.exemptFromEntitlementCheck = exempt;
- request.showProvisioningUi = false;
-
- return request;
- }
-
- @After
- public void tearDown() {
- mServiceContext.unregisterReceiver(mBroadcastReceiver);
- }
-
- private void sendWifiApStateChanged(int state) {
- final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- intent.putExtra(EXTRA_WIFI_AP_STATE, state);
- mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private void sendWifiApStateChanged(int state, String ifname, int ipmode) {
- final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- intent.putExtra(EXTRA_WIFI_AP_STATE, state);
- intent.putExtra(EXTRA_WIFI_AP_INTERFACE_NAME, ifname);
- intent.putExtra(EXTRA_WIFI_AP_MODE, ipmode);
- mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private static final String[] P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST = {
- android.Manifest.permission.ACCESS_FINE_LOCATION,
- android.Manifest.permission.ACCESS_WIFI_STATE
- };
-
- private void sendWifiP2pConnectionChanged(
- boolean isGroupFormed, boolean isGroupOwner, String ifname) {
- WifiP2pGroup group = null;
- WifiP2pInfo p2pInfo = new WifiP2pInfo();
- p2pInfo.groupFormed = isGroupFormed;
- if (isGroupFormed) {
- p2pInfo.isGroupOwner = isGroupOwner;
- group = mock(WifiP2pGroup.class);
- when(group.isGroupOwner()).thenReturn(isGroupOwner);
- when(group.getInterface()).thenReturn(ifname);
- }
-
- final Intent intent = mock(Intent.class);
- when(intent.getAction()).thenReturn(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
- when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)).thenReturn(p2pInfo);
- when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)).thenReturn(group);
-
- mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL,
- P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST);
- }
-
- private void sendUsbBroadcast(boolean connected, boolean configured, boolean function,
- int type) {
- final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
- intent.putExtra(USB_CONNECTED, connected);
- intent.putExtra(USB_CONFIGURED, configured);
- if (type == TETHERING_USB) {
- intent.putExtra(USB_FUNCTION_RNDIS, function);
- } else {
- intent.putExtra(USB_FUNCTION_NCM, function);
- }
- mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private void sendConfigurationChanged() {
- final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
- mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private void verifyInterfaceServingModeStarted(String ifname) throws Exception {
- verify(mNetd, times(1)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
- verify(mNetd, times(1)).tetherInterfaceAdd(ifname);
- verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, ifname);
- verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname),
- anyString(), anyString());
- }
-
- private void verifyTetheringBroadcast(String ifname, String whichExtra) {
- // Verify that ifname is in the whichExtra array of the tether state changed broadcast.
- final Intent bcast = mIntents.get(0);
- assertEquals(ACTION_TETHER_STATE_CHANGED, bcast.getAction());
- final ArrayList<String> ifnames = bcast.getStringArrayListExtra(whichExtra);
- assertTrue(ifnames.contains(ifname));
- mIntents.remove(bcast);
- }
-
- public void failingLocalOnlyHotspotLegacyApBroadcast(
- boolean emulateInterfaceStatusChanged) throws Exception {
- // Emulate externally-visible WifiManager effects, causing the
- // per-interface state machine to start up, and telling us that
- // hotspot mode is to be started.
- if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- }
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
- mLooper.dispatchAll();
-
- // If, and only if, Tethering received an interface status changed then
- // it creates a IpServer and sends out a broadcast indicating that the
- // interface is "available".
- if (emulateInterfaceStatusChanged) {
- // There is 1 IpServer state change event: STATE_AVAILABLE
- verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
- verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
- }
- verifyNoMoreInteractions(mNetd);
- verifyNoMoreInteractions(mWifiManager);
- }
-
- private void prepareNcmTethering() {
- // Emulate startTethering(TETHERING_NCM) called
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null);
- mLooper.dispatchAll();
- verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
-
- mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
- }
-
- private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
- initTetheringUpstream(upstreamState);
-
- // Emulate pressing the USB tethering button in Settings UI.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null);
- mLooper.dispatchAll();
- verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
-
- mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
- }
-
- @Test
- public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
- UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- prepareUsbTethering(upstreamState);
-
- // This should produce no activity of any kind.
- verifyNoMoreInteractions(mNetd);
-
- // Pretend we then receive USB configured broadcast.
- sendUsbBroadcast(true, true, true, TETHERING_USB);
- mLooper.dispatchAll();
- // Now we should see the start of tethering mechanics (in this case:
- // tetherMatchingInterfaces() which starts by fetching all interfaces).
- verify(mNetd, times(1)).interfaceGetList();
-
- // UpstreamNetworkMonitor should receive selected upstream
- verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
- verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
- }
-
- @Test
- public void failingLocalOnlyHotspotLegacyApBroadcastWithIfaceStatusChanged() throws Exception {
- failingLocalOnlyHotspotLegacyApBroadcast(true);
- }
-
- @Test
- public void failingLocalOnlyHotspotLegacyApBroadcastSansIfaceStatusChanged() throws Exception {
- failingLocalOnlyHotspotLegacyApBroadcast(false);
- }
-
- public void workingLocalOnlyHotspotEnrichedApBroadcast(
- boolean emulateInterfaceStatusChanged) throws Exception {
- // Emulate externally-visible WifiManager effects, causing the
- // per-interface state machine to start up, and telling us that
- // hotspot mode is to be started.
- if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- }
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY);
- mLooper.dispatchAll();
-
- verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
- verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
- verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
- verify(mNetd, times(1)).tetherStartWithConfiguration(any());
- verifyNoMoreInteractions(mNetd);
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
- verifyNoMoreInteractions(mWifiManager);
- verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
- verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
- verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
-
- // Emulate externally-visible WifiManager effects, when hotspot mode
- // is being torn down.
- sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
- mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
- mLooper.dispatchAll();
-
- verify(mNetd, times(1)).tetherApplyDnsInterfaces();
- verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
- verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
- // interfaceSetCfg() called once for enabling and twice disabling IPv4.
- verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
- verify(mNetd, times(1)).tetherStop();
- verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME);
- verify(mWifiManager, times(3)).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
- verifyNoMoreInteractions(mNetd);
- verifyNoMoreInteractions(mWifiManager);
- // Asking for the last error after the per-interface state machine
- // has been reaped yields an unknown interface error.
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME));
- }
-
- /**
- * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator.
- */
- private void sendIPv6TetherUpdates(UpstreamNetworkState upstreamState) {
- // IPv6TetheringCoordinator must have been notified of downstream
- verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
- argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
- eq(IpServer.STATE_TETHERED));
-
- for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) {
- UpstreamNetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
- ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
- upstreamState.linkProperties.isIpv6Provisioned()
- ? ipv6OnlyState.linkProperties
- : null);
- }
- mLooper.dispatchAll();
- }
-
- private void runUsbTethering(UpstreamNetworkState upstreamState) {
- prepareUsbTethering(upstreamState);
- sendUsbBroadcast(true, true, true, TETHERING_USB);
- mLooper.dispatchAll();
- }
-
- @Test
- public void workingMobileUsbTethering_IPv4() throws Exception {
- UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- runUsbTethering(upstreamState);
-
- verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-
- sendIPv6TetherUpdates(upstreamState);
- verify(mDadProxy, never()).setUpstreamIface(notNull());
- verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- }
-
- @Test
- public void workingMobileUsbTethering_IPv4LegacyDhcp() {
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
- true);
- sendConfigurationChanged();
- final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- runUsbTethering(upstreamState);
- sendIPv6TetherUpdates(upstreamState);
-
- verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any());
- }
-
- @Test
- public void workingMobileUsbTethering_IPv6() throws Exception {
- UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
- runUsbTethering(upstreamState);
-
- verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-
- sendIPv6TetherUpdates(upstreamState);
- // TODO: add interfaceParams to compare in verify.
- verify(mDadProxy, times(1)).setUpstreamIface(notNull());
- verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
- verify(mNetd, times(1)).tetherApplyDnsInterfaces();
- }
-
- @Test
- public void workingMobileUsbTethering_DualStack() throws Exception {
- UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
- runUsbTethering(upstreamState);
-
- verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mRouterAdvertisementDaemon, times(1)).start();
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
-
- sendIPv6TetherUpdates(upstreamState);
- verify(mDadProxy, times(1)).setUpstreamIface(notNull());
- verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
- verify(mNetd, times(1)).tetherApplyDnsInterfaces();
- }
-
- @Test
- public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
- UpstreamNetworkState upstreamState = buildMobile464xlatUpstreamState();
- runUsbTethering(upstreamState);
-
- verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
- verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-
- sendIPv6TetherUpdates(upstreamState);
- verify(mDadProxy, times(1)).setUpstreamIface(notNull());
- verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
- verify(mNetd, times(1)).tetherApplyDnsInterfaces();
- }
-
- @Test
- public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
- // Setup IPv6
- UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
- runUsbTethering(upstreamState);
-
- verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-
- // Then 464xlat comes up
- upstreamState = buildMobile464xlatUpstreamState();
- initTetheringUpstream(upstreamState);
-
- // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
- mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
- Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
- 0,
- upstreamState);
- mLooper.dispatchAll();
-
- // Forwarding is added for 464xlat
- verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
- // Forwarding was not re-added for v6 (still times(1))
- verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- // DHCP not restarted on downstream (still times(1))
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- }
-
- @Test
- public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception {
- when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
- sendConfigurationChanged();
-
- // Setup IPv6
- final UpstreamNetworkState upstreamState = buildMobileIPv6UpstreamState();
- runUsbTethering(upstreamState);
-
- // UpstreamNetworkMonitor should choose upstream automatically
- // (in this specific case: choose the default network).
- verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
- verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
-
- verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
- }
-
- private void runNcmTethering() {
- prepareNcmTethering();
- sendUsbBroadcast(true, true, true, TETHERING_NCM);
- mLooper.dispatchAll();
- }
-
- @Test
- public void workingNcmTethering() throws Exception {
- runNcmTethering();
-
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- }
-
- @Test
- public void workingNcmTethering_LegacyDhcp() {
- when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
- true);
- sendConfigurationChanged();
- runNcmTethering();
-
- verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any());
- }
-
- @Test
- public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
- workingLocalOnlyHotspotEnrichedApBroadcast(true);
- }
-
- @Test
- public void workingLocalOnlyHotspotEnrichedApBroadcastSansIfaceChanged() throws Exception {
- workingLocalOnlyHotspotEnrichedApBroadcast(false);
- }
-
- // TODO: Test with and without interfaceStatusChanged().
- @Test
- public void failingWifiTetheringLegacyApBroadcast() throws Exception {
- when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
-
- // Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
- mLooper.dispatchAll();
- verify(mWifiManager, times(1)).startTetheredHotspot(null);
- verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mNetd);
-
- // Emulate externally-visible WifiManager effects, causing the
- // per-interface state machine to start up, and telling us that
- // tethering mode is to be started.
- mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
- mLooper.dispatchAll();
-
- // There is 1 IpServer state change event: STATE_AVAILABLE
- verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
- verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
- verifyNoMoreInteractions(mNetd);
- verifyNoMoreInteractions(mWifiManager);
- }
-
- // TODO: Test with and without interfaceStatusChanged().
- @Test
- public void workingWifiTetheringEnrichedApBroadcast() throws Exception {
- when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
-
- // Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
- mLooper.dispatchAll();
- verify(mWifiManager, times(1)).startTetheredHotspot(null);
- verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mNetd);
-
- // Emulate externally-visible WifiManager effects, causing the
- // per-interface state machine to start up, and telling us that
- // tethering mode is to be started.
- mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
- mLooper.dispatchAll();
-
- verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
- verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
- verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
- verify(mNetd, times(1)).tetherStartWithConfiguration(any());
- verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME),
- anyString(), anyString());
- verifyNoMoreInteractions(mNetd);
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
- verifyNoMoreInteractions(mWifiManager);
- verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
- verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // In tethering mode, in the default configuration, an explicit request
- // for a mobile network is also made.
- verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
- // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_TETHERED
- verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
- verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI));
-
- /////
- // We do not currently emulate any upstream being found.
- //
- // This is why there are no calls to verify mNetd.tetherAddForward() or
- // mNetd.ipfwdAddInterfaceForward().
- /////
-
- // Emulate pressing the WiFi tethering button.
- mTethering.stopTethering(TETHERING_WIFI);
- mLooper.dispatchAll();
- verify(mWifiManager, times(1)).stopSoftAp();
- verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mNetd);
-
- // Emulate externally-visible WifiManager effects, when tethering mode
- // is being torn down.
- sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
- mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
- mLooper.dispatchAll();
-
- verify(mNetd, times(1)).tetherApplyDnsInterfaces();
- verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
- verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
- // interfaceSetCfg() called once for enabling and twice for disabling IPv4.
- verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
- verify(mNetd, times(1)).tetherStop();
- verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME);
- verify(mWifiManager, times(3)).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
- verifyNoMoreInteractions(mNetd);
- verifyNoMoreInteractions(mWifiManager);
- // Asking for the last error after the per-interface state machine
- // has been reaped yields an unknown interface error.
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME));
- }
-
- // TODO: Test with and without interfaceStatusChanged().
- @Test
- public void failureEnablingIpForwarding() throws Exception {
- when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
- doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
-
- // Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
- mLooper.dispatchAll();
- verify(mWifiManager, times(1)).startTetheredHotspot(null);
- verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mNetd);
-
- // Emulate externally-visible WifiManager effects, causing the
- // per-interface state machine to start up, and telling us that
- // tethering mode is to be started.
- mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
- mLooper.dispatchAll();
-
- // We verify get/set called three times here: twice for setup and once during
- // teardown because all events happen over the course of the single
- // dispatchAll() above. Note that once the IpServer IPv4 address config
- // code is refactored the two calls during shutdown will revert to one.
- verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
- verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME);
- verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
- verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME),
- anyString(), anyString());
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
- // There are 3 IpServer state change event:
- // STATE_AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
- verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
- verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI));
- verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
- // This is called, but will throw.
- verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
- // This never gets called because of the exception thrown above.
- verify(mNetd, times(0)).tetherStartWithConfiguration(any());
- // When the main state machine transitions to an error state it tells
- // downstream interfaces, which causes us to tell Wi-Fi about the error
- // so it can take down AP mode.
- verify(mNetd, times(1)).tetherApplyDnsInterfaces();
- verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
- verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
- verify(mWifiManager).updateInterfaceIpState(
- TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
-
- verifyNoMoreInteractions(mWifiManager);
- verifyNoMoreInteractions(mNetd);
- }
-
- private void runUserRestrictionsChange(
- boolean currentDisallow, boolean nextDisallow, boolean isTetheringActive,
- int expectedInteractionsWithShowNotification) throws Exception {
- final Bundle newRestrictions = new Bundle();
- newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow);
- final Tethering mockTethering = mock(Tethering.class);
- when(mockTethering.isTetheringActive()).thenReturn(isTetheringActive);
- when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions);
-
- final Tethering.UserRestrictionActionListener ural =
- new Tethering.UserRestrictionActionListener(
- mUserManager, mockTethering, mNotificationUpdater);
- ural.mDisallowTethering = currentDisallow;
-
- ural.onUserRestrictionsChanged();
-
- verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification))
- .notifyTetheringDisabledByRestriction();
- verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll();
- }
-
- @Test
- public void testDisallowTetheringWhenTetheringIsNotActive() throws Exception {
- final boolean isTetheringActive = false;
- final boolean currDisallow = false;
- final boolean nextDisallow = true;
- final int expectedInteractionsWithShowNotification = 0;
-
- runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
- expectedInteractionsWithShowNotification);
- }
-
- @Test
- public void testDisallowTetheringWhenTetheringIsActive() throws Exception {
- final boolean isTetheringActive = true;
- final boolean currDisallow = false;
- final boolean nextDisallow = true;
- final int expectedInteractionsWithShowNotification = 1;
-
- runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
- expectedInteractionsWithShowNotification);
- }
-
- @Test
- public void testAllowTetheringWhenTetheringIsNotActive() throws Exception {
- final boolean isTetheringActive = false;
- final boolean currDisallow = true;
- final boolean nextDisallow = false;
- final int expectedInteractionsWithShowNotification = 0;
-
- runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
- expectedInteractionsWithShowNotification);
- }
-
- @Test
- public void testAllowTetheringWhenTetheringIsActive() throws Exception {
- final boolean isTetheringActive = true;
- final boolean currDisallow = true;
- final boolean nextDisallow = false;
- final int expectedInteractionsWithShowNotification = 0;
-
- runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
- expectedInteractionsWithShowNotification);
- }
-
- @Test
- public void testDisallowTetheringUnchanged() throws Exception {
- final boolean isTetheringActive = true;
- final int expectedInteractionsWithShowNotification = 0;
- boolean currDisallow = true;
- boolean nextDisallow = true;
-
- runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
- expectedInteractionsWithShowNotification);
-
- currDisallow = false;
- nextDisallow = false;
-
- runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
- expectedInteractionsWithShowNotification);
- }
-
- private class TestTetheringEventCallback extends ITetheringEventCallback.Stub {
- private final ArrayList<Network> mActualUpstreams = new ArrayList<>();
- private final ArrayList<TetheringConfigurationParcel> mTetheringConfigs =
- new ArrayList<>();
- private final ArrayList<TetherStatesParcel> mTetherStates = new ArrayList<>();
- private final ArrayList<Integer> mOffloadStatus = new ArrayList<>();
-
- // This function will remove the recorded callbacks, so it must be called once for
- // each callback. If this is called after multiple callback, the order matters.
- // onCallbackCreated counts as the first call to expectUpstreamChanged with
- // @see onCallbackCreated.
- public void expectUpstreamChanged(Network... networks) {
- if (networks == null) {
- assertNoUpstreamChangeCallback();
- return;
- }
-
- final ArrayList<Network> expectedUpstreams =
- new ArrayList<Network>(Arrays.asList(networks));
- for (Network upstream : expectedUpstreams) {
- // throws OOB if no expectations
- assertEquals(mActualUpstreams.remove(0), upstream);
- }
- assertNoUpstreamChangeCallback();
- }
-
- // This function will remove the recorded callbacks, so it must be called once
- // for each callback. If this is called after multiple callback, the order matters.
- // onCallbackCreated counts as the first call to onConfigurationChanged with
- // @see onCallbackCreated.
- public void expectConfigurationChanged(TetheringConfigurationParcel... tetherConfigs) {
- final ArrayList<TetheringConfigurationParcel> expectedTetherConfig =
- new ArrayList<TetheringConfigurationParcel>(Arrays.asList(tetherConfigs));
- for (TetheringConfigurationParcel config : expectedTetherConfig) {
- // throws OOB if no expectations
- final TetheringConfigurationParcel actualConfig = mTetheringConfigs.remove(0);
- assertTetherConfigParcelEqual(actualConfig, config);
- }
- assertNoConfigChangeCallback();
- }
-
- public void expectOffloadStatusChanged(final int expectedStatus) {
- assertOffloadStatusChangedCallback();
- assertEquals(mOffloadStatus.remove(0), new Integer(expectedStatus));
- }
-
- public TetherStatesParcel pollTetherStatesChanged() {
- assertStateChangeCallback();
- return mTetherStates.remove(0);
- }
-
- @Override
- public void onUpstreamChanged(Network network) {
- mActualUpstreams.add(network);
- }
-
- @Override
- public void onConfigurationChanged(TetheringConfigurationParcel config) {
- mTetheringConfigs.add(config);
- }
-
- @Override
- public void onTetherStatesChanged(TetherStatesParcel states) {
- mTetherStates.add(states);
- }
-
- @Override
- public void onTetherClientsChanged(List<TetheredClient> clients) {
- // TODO: check this
- }
-
- @Override
- public void onOffloadStatusChanged(final int status) {
- mOffloadStatus.add(status);
- }
-
- @Override
- public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
- mActualUpstreams.add(parcel.upstreamNetwork);
- mTetheringConfigs.add(parcel.config);
- mTetherStates.add(parcel.states);
- mOffloadStatus.add(parcel.offloadStatus);
- }
-
- @Override
- public void onCallbackStopped(int errorCode) { }
-
- public void assertNoUpstreamChangeCallback() {
- assertTrue(mActualUpstreams.isEmpty());
- }
-
- public void assertNoConfigChangeCallback() {
- assertTrue(mTetheringConfigs.isEmpty());
- }
-
- public void assertNoStateChangeCallback() {
- assertTrue(mTetherStates.isEmpty());
- }
-
- public void assertStateChangeCallback() {
- assertFalse(mTetherStates.isEmpty());
- }
-
- public void assertOffloadStatusChangedCallback() {
- assertFalse(mOffloadStatus.isEmpty());
- }
-
- public void assertNoCallback() {
- assertNoUpstreamChangeCallback();
- assertNoConfigChangeCallback();
- assertNoStateChangeCallback();
- }
-
- private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual,
- @NonNull TetheringConfigurationParcel expect) {
- assertEquals(actual.subId, expect.subId);
- assertArrayEquals(actual.tetherableUsbRegexs, expect.tetherableUsbRegexs);
- assertArrayEquals(actual.tetherableWifiRegexs, expect.tetherableWifiRegexs);
- assertArrayEquals(actual.tetherableBluetoothRegexs, expect.tetherableBluetoothRegexs);
- assertEquals(actual.isDunRequired, expect.isDunRequired);
- assertEquals(actual.chooseUpstreamAutomatically, expect.chooseUpstreamAutomatically);
- assertArrayEquals(actual.preferredUpstreamIfaceTypes,
- expect.preferredUpstreamIfaceTypes);
- assertArrayEquals(actual.legacyDhcpRanges, expect.legacyDhcpRanges);
- assertArrayEquals(actual.defaultIPv4DNS, expect.defaultIPv4DNS);
- assertEquals(actual.enableLegacyDhcpServer, expect.enableLegacyDhcpServer);
- assertArrayEquals(actual.provisioningApp, expect.provisioningApp);
- assertEquals(actual.provisioningAppNoUi, expect.provisioningAppNoUi);
- assertEquals(actual.provisioningCheckPeriod, expect.provisioningCheckPeriod);
- }
- }
-
- private void assertTetherStatesNotNullButEmpty(final TetherStatesParcel parcel) {
- assertFalse(parcel == null);
- assertEquals(0, parcel.availableList.length);
- assertEquals(0, parcel.tetheredList.length);
- assertEquals(0, parcel.localOnlyList.length);
- assertEquals(0, parcel.erroredIfaceList.length);
- assertEquals(0, parcel.lastErrorList.length);
- MiscAsserts.assertFieldCountEquals(5, TetherStatesParcel.class);
- }
-
- @Test
- public void testRegisterTetheringEventCallback() throws Exception {
- TestTetheringEventCallback callback = new TestTetheringEventCallback();
- TestTetheringEventCallback callback2 = new TestTetheringEventCallback();
-
- // 1. Register one callback before running any tethering.
- mTethering.registerTetheringEventCallback(callback);
- mLooper.dispatchAll();
- callback.expectUpstreamChanged(new Network[] {null});
- callback.expectConfigurationChanged(
- mTethering.getTetheringConfiguration().toStableParcelable());
- TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
- assertTetherStatesNotNullButEmpty(tetherState);
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
- // 2. Enable wifi tethering.
- UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
- initTetheringUpstream(upstreamState);
- when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
- mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
- mLooper.dispatchAll();
- tetherState = callback.pollTetherStatesChanged();
- assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
-
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
- sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
- mLooper.dispatchAll();
- tetherState = callback.pollTetherStatesChanged();
- assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
- callback.expectUpstreamChanged(upstreamState.network);
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
-
- // 3. Register second callback.
- mTethering.registerTetheringEventCallback(callback2);
- mLooper.dispatchAll();
- callback2.expectUpstreamChanged(upstreamState.network);
- callback2.expectConfigurationChanged(
- mTethering.getTetheringConfiguration().toStableParcelable());
- tetherState = callback2.pollTetherStatesChanged();
- assertEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
- callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
-
- // 4. Unregister first callback and disable wifi tethering
- mTethering.unregisterTetheringEventCallback(callback);
- mLooper.dispatchAll();
- mTethering.stopTethering(TETHERING_WIFI);
- sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
- mLooper.dispatchAll();
- tetherState = callback2.pollTetherStatesChanged();
- assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
- mLooper.dispatchAll();
- callback2.expectUpstreamChanged(new Network[] {null});
- callback2.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
- callback.assertNoCallback();
- }
-
- @Test
- public void testReportFailCallbackIfOffloadNotSupported() throws Exception {
- final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
- TestTetheringEventCallback callback = new TestTetheringEventCallback();
- mTethering.registerTetheringEventCallback(callback);
- mLooper.dispatchAll();
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
-
- // 1. Offload fail if no OffloadConfig.
- initOffloadConfiguration(false /* offloadConfig */, true /* offloadControl */,
- 0 /* defaultDisabled */);
- runUsbTethering(upstreamState);
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
- runStopUSBTethering();
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
- reset(mUsbManager);
- // 2. Offload fail if no OffloadControl.
- initOffloadConfiguration(true /* offloadConfig */, false /* offloadControl */,
- 0 /* defaultDisabled */);
- runUsbTethering(upstreamState);
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
- runStopUSBTethering();
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
- reset(mUsbManager);
- // 3. Offload fail if disabled by settings.
- initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */,
- 1 /* defaultDisabled */);
- runUsbTethering(upstreamState);
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
- runStopUSBTethering();
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
- }
-
- private void runStopUSBTethering() {
- mTethering.stopTethering(TETHERING_USB);
- mLooper.dispatchAll();
- mTethering.interfaceRemoved(TEST_USB_IFNAME);
- mLooper.dispatchAll();
- }
-
- private void initOffloadConfiguration(final boolean offloadConfig,
- final boolean offloadControl, final int defaultDisabled) {
- when(mOffloadHardwareInterface.initOffloadConfig()).thenReturn(offloadConfig);
- when(mOffloadHardwareInterface.initOffloadControl(any())).thenReturn(offloadControl);
- when(mOffloadHardwareInterface.getDefaultTetherOffloadDisabled()).thenReturn(
- defaultDisabled);
- }
-
- @Test
- public void testMultiSimAware() throws Exception {
- final TetheringConfiguration initailConfig = mTethering.getTetheringConfiguration();
- assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.activeDataSubId);
-
- final int fakeSubId = 1234;
- mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
- final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration();
- assertEquals(fakeSubId, newConfig.activeDataSubId);
- verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId));
- }
-
- @Test
- public void testNoDuplicatedEthernetRequest() throws Exception {
- final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
- when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
- mLooper.dispatchAll();
- verify(mEm, times(1)).requestTetheredInterface(any(), any());
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
- mLooper.dispatchAll();
- verifyNoMoreInteractions(mEm);
- mTethering.stopTethering(TETHERING_ETHERNET);
- mLooper.dispatchAll();
- verify(mockRequest, times(1)).release();
- mTethering.stopTethering(TETHERING_ETHERNET);
- mLooper.dispatchAll();
- verifyNoMoreInteractions(mEm);
- }
-
- private void workingWifiP2pGroupOwner(
- boolean emulateInterfaceStatusChanged) throws Exception {
- if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
- }
- sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
- mLooper.dispatchAll();
-
- verifyInterfaceServingModeStarted(TEST_P2P_IFNAME);
- verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
- verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
- verify(mNetd, times(1)).tetherStartWithConfiguration(any());
- verifyNoMoreInteractions(mNetd);
- verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
- verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
- verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
-
- assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME));
-
- // Emulate externally-visible WifiP2pManager effects, when wifi p2p group
- // is being removed.
- sendWifiP2pConnectionChanged(false, true, TEST_P2P_IFNAME);
- mTethering.interfaceRemoved(TEST_P2P_IFNAME);
- mLooper.dispatchAll();
-
- verify(mNetd, times(1)).tetherApplyDnsInterfaces();
- verify(mNetd, times(1)).tetherInterfaceRemove(TEST_P2P_IFNAME);
- verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
- // interfaceSetCfg() called once for enabling and twice for disabling IPv4.
- verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
- verify(mNetd, times(1)).tetherStop();
- verify(mNetd, times(1)).ipfwdDisableForwarding(TETHERING_NAME);
- verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream();
- verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
- verifyNoMoreInteractions(mNetd);
- // Asking for the last error after the per-interface state machine
- // has been reaped yields an unknown interface error.
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
- }
-
- private void workingWifiP2pGroupClient(
- boolean emulateInterfaceStatusChanged) throws Exception {
- if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
- }
- sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME);
- mLooper.dispatchAll();
-
- verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
- verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME);
- verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
- verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME);
- verify(mNetd, never()).tetherStartWithConfiguration(any());
-
- // Emulate externally-visible WifiP2pManager effects, when wifi p2p group
- // is being removed.
- sendWifiP2pConnectionChanged(false, false, TEST_P2P_IFNAME);
- mTethering.interfaceRemoved(TEST_P2P_IFNAME);
- mLooper.dispatchAll();
-
- verify(mNetd, never()).tetherApplyDnsInterfaces();
- verify(mNetd, never()).tetherInterfaceRemove(TEST_P2P_IFNAME);
- verify(mNetd, never()).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
- verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
- verify(mNetd, never()).tetherStop();
- verify(mNetd, never()).ipfwdDisableForwarding(TETHERING_NAME);
- verifyNoMoreInteractions(mNetd);
- // Asking for the last error after the per-interface state machine
- // has been reaped yields an unknown interface error.
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
- }
-
- @Test
- public void workingWifiP2pGroupOwnerWithIfaceChanged() throws Exception {
- workingWifiP2pGroupOwner(true);
- }
-
- @Test
- public void workingWifiP2pGroupOwnerSansIfaceChanged() throws Exception {
- workingWifiP2pGroupOwner(false);
- }
-
- private void workingWifiP2pGroupOwnerLegacyMode(
- boolean emulateInterfaceStatusChanged) throws Exception {
- // change to legacy mode and update tethering information by chaning SIM
- when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
- .thenReturn(new String[]{});
- final int fakeSubId = 1234;
- mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
-
- if (emulateInterfaceStatusChanged) {
- mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
- }
- sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
- mLooper.dispatchAll();
-
- verify(mNetd, never()).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
- verify(mNetd, never()).tetherInterfaceAdd(TEST_P2P_IFNAME);
- verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
- verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME);
- verify(mNetd, never()).tetherStartWithConfiguration(any());
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
- }
- @Test
- public void workingWifiP2pGroupOwnerLegacyModeWithIfaceChanged() throws Exception {
- workingWifiP2pGroupOwnerLegacyMode(true);
- }
-
- @Test
- public void workingWifiP2pGroupOwnerLegacyModeSansIfaceChanged() throws Exception {
- workingWifiP2pGroupOwnerLegacyMode(false);
- }
-
- @Test
- public void workingWifiP2pGroupClientWithIfaceChanged() throws Exception {
- workingWifiP2pGroupClient(true);
- }
-
- @Test
- public void workingWifiP2pGroupClientSansIfaceChanged() throws Exception {
- workingWifiP2pGroupClient(false);
- }
-
- private void setDataSaverEnabled(boolean enabled) {
- final Intent intent = new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED);
- mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-
- final int status = enabled ? RESTRICT_BACKGROUND_STATUS_ENABLED
- : RESTRICT_BACKGROUND_STATUS_DISABLED;
- when(mCm.getRestrictBackgroundStatus()).thenReturn(status);
- mLooper.dispatchAll();
- }
-
- @Test
- public void testDataSaverChanged() {
- // Start Tethering.
- final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- runUsbTethering(upstreamState);
- assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
- // Data saver is ON.
- setDataSaverEnabled(true);
- // Verify that tethering should be disabled.
- verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- mTethering.interfaceRemoved(TEST_USB_IFNAME);
- mLooper.dispatchAll();
- assertEquals(mTethering.getTetheredIfaces(), new String[0]);
- reset(mUsbManager);
-
- runUsbTethering(upstreamState);
- // Verify that user can start tethering again without turning OFF data saver.
- assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
-
- // If data saver is keep ON with change event, tethering should not be OFF this time.
- setDataSaverEnabled(true);
- verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
-
- // If data saver is turned OFF, it should not change tethering.
- setDataSaverEnabled(false);
- verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
- }
-
- private static <T> void assertContains(Collection<T> collection, T element) {
- assertTrue(element + " not found in " + collection, collection.contains(element));
- }
-
- private class ResultListener extends IIntResultListener.Stub {
- private final int mExpectedResult;
- private boolean mHasResult = false;
- ResultListener(final int expectedResult) {
- mExpectedResult = expectedResult;
- }
-
- @Override
- public void onResult(final int resultCode) {
- mHasResult = true;
- if (resultCode != mExpectedResult) {
- fail("expected result: " + mExpectedResult + " but actual result: " + resultCode);
- }
- }
-
- public void assertHasResult() {
- if (!mHasResult) fail("No callback result");
- }
- }
-
- @Test
- public void testMultipleStartTethering() throws Exception {
- final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24");
- final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24");
- final String serverAddr = "192.168.20.1";
- final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR);
-
- // Enable USB tethering and check that Tethering starts USB.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
- null, null, false), firstResult);
- mLooper.dispatchAll();
- firstResult.assertHasResult();
- verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
- verifyNoMoreInteractions(mUsbManager);
-
- // Enable USB tethering again with the same request and expect no change to USB.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
- null, null, false), secondResult);
- mLooper.dispatchAll();
- secondResult.assertHasResult();
- verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- reset(mUsbManager);
-
- // Enable USB tethering with a different request and expect that USB is stopped and
- // started.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
- serverLinkAddr, clientLinkAddr, false), thirdResult);
- mLooper.dispatchAll();
- thirdResult.assertHasResult();
- verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
-
- // Expect that when USB comes up, the DHCP server is configured with the requested address.
- mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
- sendUsbBroadcast(true, true, true, TETHERING_USB);
- mLooper.dispatchAll();
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
- }
-
- @Test
- public void testRequestStaticIp() throws Exception {
- final LinkAddress serverLinkAddr = new LinkAddress("192.168.0.123/24");
- final LinkAddress clientLinkAddr = new LinkAddress("192.168.0.42/24");
- final String serverAddr = "192.168.0.123";
- final int clientAddrParceled = 0xc0a8002a;
- final ArgumentCaptor<DhcpServingParamsParcel> dhcpParamsCaptor =
- ArgumentCaptor.forClass(DhcpServingParamsParcel.class);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
- serverLinkAddr, clientLinkAddr, false), null);
- mLooper.dispatchAll();
- verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
- mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
- sendUsbBroadcast(true, true, true, TETHERING_USB);
- mLooper.dispatchAll();
- verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
- verify(mIpServerDependencies, times(1)).makeDhcpServer(any(), dhcpParamsCaptor.capture(),
- any());
- final DhcpServingParamsParcel params = dhcpParamsCaptor.getValue();
- assertEquals(serverAddr, intToInet4AddressHTH(params.serverAddr).getHostAddress());
- assertEquals(24, params.serverAddrPrefixLength);
- assertEquals(clientAddrParceled, params.singleClientAddr);
- }
-
- @Test
- public void testUpstreamNetworkChanged() {
- final Tethering.TetherMainSM stateMachine = (Tethering.TetherMainSM)
- mTetheringDependencies.mUpstreamNetworkMonitorSM;
- final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- initTetheringUpstream(upstreamState);
- stateMachine.chooseUpstreamType(true);
-
- verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
- verify(mNotificationUpdater, times(1)).onUpstreamCapabilitiesChanged(any());
- }
-
- @Test
- public void testUpstreamCapabilitiesChanged() {
- final Tethering.TetherMainSM stateMachine = (Tethering.TetherMainSM)
- mTetheringDependencies.mUpstreamNetworkMonitorSM;
- final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
- initTetheringUpstream(upstreamState);
- stateMachine.chooseUpstreamType(true);
-
- stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
- // Should have two onUpstreamCapabilitiesChanged().
- // One is called by reportUpstreamChanged(). One is called by EVENT_ON_CAPABILITIES.
- verify(mNotificationUpdater, times(2)).onUpstreamCapabilitiesChanged(any());
- reset(mNotificationUpdater);
-
- // Verify that onUpstreamCapabilitiesChanged won't be called if not current upstream network
- // capabilities changed.
- final UpstreamNetworkState upstreamState2 = new UpstreamNetworkState(
- upstreamState.linkProperties, upstreamState.networkCapabilities, new Network(101));
- stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2);
- verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any());
- }
-
- @Test
- public void testDumpTetheringLog() throws Exception {
- final FileDescriptor mockFd = mock(FileDescriptor.class);
- final PrintWriter mockPw = mock(PrintWriter.class);
- runUsbTethering(null);
- mLooper.startAutoDispatch();
- mTethering.dump(mockFd, mockPw, new String[0]);
- verify(mConfig).dump(any());
- verify(mEntitleMgr).dump(any());
- verify(mOffloadCtrl).dump(any());
- mLooper.stopAutoDispatch();
- }
-
- @Test
- public void testExemptFromEntitlementCheck() throws Exception {
- setupForRequiredProvisioning();
- final TetheringRequestParcel wifiNotExemptRequest =
- createTetheringRequestParcel(TETHERING_WIFI, null, null, false);
- mTethering.startTethering(wifiNotExemptRequest, null);
- mLooper.dispatchAll();
- verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false);
- verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI);
- assertFalse(mEntitleMgr.isCellularUpstreamPermitted());
- mTethering.stopTethering(TETHERING_WIFI);
- mLooper.dispatchAll();
- verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI);
- reset(mEntitleMgr);
-
- setupForRequiredProvisioning();
- final TetheringRequestParcel wifiExemptRequest =
- createTetheringRequestParcel(TETHERING_WIFI, null, null, true);
- mTethering.startTethering(wifiExemptRequest, null);
- mLooper.dispatchAll();
- verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false);
- verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI);
- assertTrue(mEntitleMgr.isCellularUpstreamPermitted());
- mTethering.stopTethering(TETHERING_WIFI);
- mLooper.dispatchAll();
- verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI);
- reset(mEntitleMgr);
-
- // If one app enables tethering without provisioning check first, then another app enables
- // tethering of the same type but does not disable the provisioning check.
- setupForRequiredProvisioning();
- mTethering.startTethering(wifiExemptRequest, null);
- mLooper.dispatchAll();
- verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false);
- verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI);
- assertTrue(mEntitleMgr.isCellularUpstreamPermitted());
- reset(mEntitleMgr);
- setupForRequiredProvisioning();
- mTethering.startTethering(wifiNotExemptRequest, null);
- mLooper.dispatchAll();
- verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false);
- verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI);
- assertFalse(mEntitleMgr.isCellularUpstreamPermitted());
- mTethering.stopTethering(TETHERING_WIFI);
- mLooper.dispatchAll();
- verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI);
- reset(mEntitleMgr);
- }
-
- private void setupForRequiredProvisioning() {
- // Produce some acceptable looking provision app setting if requested.
- when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
- .thenReturn(PROVISIONING_APP_NAME);
- when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
- .thenReturn(PROVISIONING_NO_UI_APP_NAME);
- // Act like the CarrierConfigManager is present and ready unless told otherwise.
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(mCarrierConfigManager);
- when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
- sendConfigurationChanged();
- }
-
- private static UpstreamNetworkState buildV4UpstreamState(final LinkAddress address,
- final Network network, final String iface, final int transportType) {
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(iface);
-
- prop.addLinkAddress(address);
-
- final NetworkCapabilities capabilities = new NetworkCapabilities()
- .addTransportType(transportType);
- return new UpstreamNetworkState(prop, capabilities, network);
- }
-
- private void updateV4Upstream(final LinkAddress ipv4Address, final Network network,
- final String iface, final int transportType) {
- final UpstreamNetworkState upstream = buildV4UpstreamState(ipv4Address, network, iface,
- transportType);
- mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
- Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
- 0,
- upstream);
- mLooper.dispatchAll();
- }
-
- @Test
- public void testHandleIpConflict() throws Exception {
- final Network wifiNetwork = new Network(200);
- final Network[] allNetworks = { wifiNetwork };
- when(mCm.getAllNetworks()).thenReturn(allNetworks);
- runUsbTethering(null);
- final ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor =
- ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
- verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture());
- final String ipv4Address = ifaceConfigCaptor.getValue().ipv4Addr;
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- reset(mNetd, mUsbManager);
-
- // Cause a prefix conflict by assigning a /30 out of the downstream's /24 to the upstream.
- updateV4Upstream(new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address), 30),
- wifiNetwork, TEST_WIFI_IFNAME, TRANSPORT_WIFI);
- // verify turn off usb tethering
- verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- mTethering.interfaceRemoved(TEST_USB_IFNAME);
- mLooper.dispatchAll();
- // verify restart usb tethering
- verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
- }
-
- @Test
- public void testNoAddressAvailable() throws Exception {
- final Network wifiNetwork = new Network(200);
- final Network btNetwork = new Network(201);
- final Network mobileNetwork = new Network(202);
- final Network[] allNetworks = { wifiNetwork, btNetwork, mobileNetwork };
- when(mCm.getAllNetworks()).thenReturn(allNetworks);
- runUsbTethering(null);
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
- any(), any());
- reset(mUsbManager);
- final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
- when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
- final ArgumentCaptor<TetheredInterfaceCallback> callbackCaptor =
- ArgumentCaptor.forClass(TetheredInterfaceCallback.class);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
- mLooper.dispatchAll();
- verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
- TetheredInterfaceCallback ethCallback = callbackCaptor.getValue();
- ethCallback.onAvailable(TEST_ETH_IFNAME);
- mLooper.dispatchAll();
- reset(mUsbManager, mEm);
-
- updateV4Upstream(new LinkAddress("192.168.0.100/16"), wifiNetwork, TEST_WIFI_IFNAME,
- TRANSPORT_WIFI);
- updateV4Upstream(new LinkAddress("172.16.0.0/12"), btNetwork, TEST_BT_IFNAME,
- TRANSPORT_BLUETOOTH);
- updateV4Upstream(new LinkAddress("10.0.0.0/8"), mobileNetwork, TEST_MOBILE_IFNAME,
- TRANSPORT_CELLULAR);
-
- mLooper.dispatchAll();
- // verify turn off usb tethering
- verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- // verify turn off ethernet tethering
- verify(mockRequest).release();
- mTethering.interfaceRemoved(TEST_USB_IFNAME);
- ethCallback.onUnavailable();
- mLooper.dispatchAll();
- // verify restart usb tethering
- verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
- // verify restart ethernet tethering
- verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
- ethCallback = callbackCaptor.getValue();
- ethCallback.onAvailable(TEST_ETH_IFNAME);
-
- reset(mUsbManager, mEm);
- when(mNetd.interfaceGetList())
- .thenReturn(new String[] {
- TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
- TEST_NCM_IFNAME, TEST_ETH_IFNAME});
-
- mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
- sendUsbBroadcast(true, true, true, TETHERING_USB);
- mLooper.dispatchAll();
- assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME);
- assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME);
- assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME));
- assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME));
- }
-
- @Test
- public void testProvisioningNeededButUnavailable() throws Exception {
- assertTrue(mTethering.isTetheringSupported());
- verify(mPackageManager, never()).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES);
-
- setupForRequiredProvisioning();
- assertTrue(mTethering.isTetheringSupported());
- verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES);
- reset(mPackageManager);
-
- doThrow(PackageManager.NameNotFoundException.class).when(mPackageManager).getPackageInfo(
- PROVISIONING_APP_NAME[0], GET_ACTIVITIES);
- setupForRequiredProvisioning();
- assertFalse(mTethering.isTetheringSupported());
- verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES);
- }
-
- // TODO: Test that a request for hotspot mode doesn't interfere with an
- // already operating tethering mode interface.
-}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
deleted file mode 100644
index 232588c7eec0..000000000000
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
+++ /dev/null
@@ -1,800 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-
-import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.IConnectivityManager;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.Message;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class UpstreamNetworkMonitorTest {
- private static final int EVENT_UNM_UPDATE = 1;
-
- private static final boolean INCLUDES = true;
- private static final boolean EXCLUDES = false;
-
- // Actual contents of the request don't matter for this test. The lack of
- // any specific TRANSPORT_* is sufficient to identify this request.
- private static final NetworkRequest sDefaultRequest = new NetworkRequest.Builder().build();
-
- @Mock private Context mContext;
- @Mock private EntitlementManager mEntitleMgr;
- @Mock private IConnectivityManager mCS;
- @Mock private SharedLog mLog;
-
- private TestStateMachine mSM;
- private TestConnectivityManager mCM;
- private UpstreamNetworkMonitor mUNM;
-
- @Before public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- reset(mContext);
- reset(mCS);
- reset(mLog);
- when(mLog.forSubComponent(anyString())).thenReturn(mLog);
- when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
-
- mCM = spy(new TestConnectivityManager(mContext, mCS));
- mSM = new TestStateMachine();
- mUNM = new UpstreamNetworkMonitor(
- (ConnectivityManager) mCM, mSM, mLog, EVENT_UNM_UPDATE);
- }
-
- @After public void tearDown() throws Exception {
- if (mSM != null) {
- mSM.quit();
- mSM = null;
- }
- }
-
- @Test
- public void testStopWithoutStartIsNonFatal() {
- mUNM.stop();
- mUNM.stop();
- mUNM.stop();
- }
-
- @Test
- public void testDoesNothingBeforeTrackDefaultAndStarted() throws Exception {
- assertTrue(mCM.hasNoCallbacks());
- assertFalse(mUNM.mobileNetworkRequested());
-
- mUNM.updateMobileRequiresDun(true);
- assertTrue(mCM.hasNoCallbacks());
- mUNM.updateMobileRequiresDun(false);
- assertTrue(mCM.hasNoCallbacks());
- }
-
- @Test
- public void testDefaultNetworkIsTracked() throws Exception {
- assertTrue(mCM.hasNoCallbacks());
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
-
- mUNM.startObserveAllNetworks();
- assertEquals(1, mCM.trackingDefault.size());
-
- mUNM.stop();
- assertTrue(mCM.onlyHasDefaultCallbacks());
- }
-
- @Test
- public void testListensForAllNetworks() throws Exception {
- assertTrue(mCM.listening.isEmpty());
-
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
- mUNM.startObserveAllNetworks();
- assertFalse(mCM.listening.isEmpty());
- assertTrue(mCM.isListeningForAll());
-
- mUNM.stop();
- assertTrue(mCM.onlyHasDefaultCallbacks());
- }
-
- @Test
- public void testCallbacksRegistered() {
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
- verify(mCM, times(1)).requestNetwork(
- eq(sDefaultRequest), any(NetworkCallback.class), any(Handler.class));
- mUNM.startObserveAllNetworks();
- verify(mCM, times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
-
- mUNM.stop();
- verify(mCM, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
- }
-
- @Test
- public void testRequestsMobileNetwork() throws Exception {
- assertFalse(mUNM.mobileNetworkRequested());
- assertEquals(0, mCM.requested.size());
-
- mUNM.startObserveAllNetworks();
- assertFalse(mUNM.mobileNetworkRequested());
- assertEquals(0, mCM.requested.size());
-
- mUNM.updateMobileRequiresDun(false);
- assertFalse(mUNM.mobileNetworkRequested());
- assertEquals(0, mCM.requested.size());
-
- mUNM.registerMobileNetworkRequest();
- assertTrue(mUNM.mobileNetworkRequested());
- assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
- assertFalse(isDunRequested());
-
- mUNM.stop();
- assertFalse(mUNM.mobileNetworkRequested());
- assertTrue(mCM.hasNoCallbacks());
- }
-
- @Test
- public void testDuplicateMobileRequestsIgnored() throws Exception {
- assertFalse(mUNM.mobileNetworkRequested());
- assertEquals(0, mCM.requested.size());
-
- mUNM.startObserveAllNetworks();
- verify(mCM, times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
- assertFalse(mUNM.mobileNetworkRequested());
- assertEquals(0, mCM.requested.size());
-
- mUNM.updateMobileRequiresDun(true);
- mUNM.registerMobileNetworkRequest();
- verify(mCM, times(1)).requestNetwork(
- any(NetworkRequest.class), anyInt(), anyInt(), any(Handler.class),
- any(NetworkCallback.class));
-
- assertTrue(mUNM.mobileNetworkRequested());
- assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
- assertTrue(isDunRequested());
-
- // Try a few things that must not result in any state change.
- mUNM.registerMobileNetworkRequest();
- mUNM.updateMobileRequiresDun(true);
- mUNM.registerMobileNetworkRequest();
-
- assertTrue(mUNM.mobileNetworkRequested());
- assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
- assertTrue(isDunRequested());
-
- mUNM.stop();
- verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
-
- verifyNoMoreInteractions(mCM);
- }
-
- @Test
- public void testRequestsDunNetwork() throws Exception {
- assertFalse(mUNM.mobileNetworkRequested());
- assertEquals(0, mCM.requested.size());
-
- mUNM.startObserveAllNetworks();
- assertFalse(mUNM.mobileNetworkRequested());
- assertEquals(0, mCM.requested.size());
-
- mUNM.updateMobileRequiresDun(true);
- assertFalse(mUNM.mobileNetworkRequested());
- assertEquals(0, mCM.requested.size());
-
- mUNM.registerMobileNetworkRequest();
- assertTrue(mUNM.mobileNetworkRequested());
- assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
- assertTrue(isDunRequested());
-
- mUNM.stop();
- assertFalse(mUNM.mobileNetworkRequested());
- assertTrue(mCM.hasNoCallbacks());
- }
-
- @Test
- public void testUpdateMobileRequiresDun() throws Exception {
- mUNM.startObserveAllNetworks();
-
- // Test going from no-DUN to DUN correctly re-registers callbacks.
- mUNM.updateMobileRequiresDun(false);
- mUNM.registerMobileNetworkRequest();
- assertTrue(mUNM.mobileNetworkRequested());
- assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
- assertFalse(isDunRequested());
- mUNM.updateMobileRequiresDun(true);
- assertTrue(mUNM.mobileNetworkRequested());
- assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
- assertTrue(isDunRequested());
-
- // Test going from DUN to no-DUN correctly re-registers callbacks.
- mUNM.updateMobileRequiresDun(false);
- assertTrue(mUNM.mobileNetworkRequested());
- assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
- assertFalse(isDunRequested());
-
- mUNM.stop();
- assertFalse(mUNM.mobileNetworkRequested());
- }
-
- @Test
- public void testSelectPreferredUpstreamType() throws Exception {
- final Collection<Integer> preferredTypes = new ArrayList<>();
- preferredTypes.add(TYPE_WIFI);
-
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
- mUNM.startObserveAllNetworks();
- // There are no networks, so there is nothing to select.
- assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
-
- final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
- wifiAgent.fakeConnect();
- // WiFi is up, we should prefer it.
- assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
- wifiAgent.fakeDisconnect();
- // There are no networks, so there is nothing to select.
- assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
-
- final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- cellAgent.fakeConnect();
- assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
-
- preferredTypes.add(TYPE_MOBILE_DUN);
- // This is coupled with preferred types in TetheringConfiguration.
- mUNM.updateMobileRequiresDun(true);
- // DUN is available, but only use regular cell: no upstream selected.
- assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
- preferredTypes.remove(TYPE_MOBILE_DUN);
- // No WiFi, but our preferred flavour of cell is up.
- preferredTypes.add(TYPE_MOBILE_HIPRI);
- // This is coupled with preferred types in TetheringConfiguration.
- mUNM.updateMobileRequiresDun(false);
- assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
- mUNM.selectPreferredUpstreamType(preferredTypes));
- // Check to see we filed an explicit request.
- assertEquals(1, mCM.requested.size());
- NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
- assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
- assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
- // mobile is not permitted, we should not use HIPRI.
- when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
- assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
- assertEquals(0, mCM.requested.size());
- // mobile change back to permitted, HIRPI should come back
- when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
- assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
- mUNM.selectPreferredUpstreamType(preferredTypes));
-
- wifiAgent.fakeConnect();
- // WiFi is up, and we should prefer it over cell.
- assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
- assertEquals(0, mCM.requested.size());
-
- preferredTypes.remove(TYPE_MOBILE_HIPRI);
- preferredTypes.add(TYPE_MOBILE_DUN);
- // This is coupled with preferred types in TetheringConfiguration.
- mUNM.updateMobileRequiresDun(true);
- assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
-
- final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
- dunAgent.fakeConnect();
-
- // WiFi is still preferred.
- assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
-
- // WiFi goes down, cell and DUN are still up but only DUN is preferred.
- wifiAgent.fakeDisconnect();
- assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
- mUNM.selectPreferredUpstreamType(preferredTypes));
- // Check to see we filed an explicit request.
- assertEquals(1, mCM.requested.size());
- netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
- assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
- assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
- // mobile is not permitted, we should not use DUN.
- when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
- assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
- assertEquals(0, mCM.requested.size());
- // mobile change back to permitted, DUN should come back
- when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
- assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
- mUNM.selectPreferredUpstreamType(preferredTypes));
- }
-
- @Test
- public void testGetCurrentPreferredUpstream() throws Exception {
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
- mUNM.startObserveAllNetworks();
- mUNM.updateMobileRequiresDun(false);
-
- // [0] Mobile connects, DUN not required -> mobile selected.
- final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- cellAgent.fakeConnect();
- mCM.makeDefaultNetwork(cellAgent);
- assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
-
- // [1] Mobile connects but not permitted -> null selected
- when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
- assertEquals(null, mUNM.getCurrentPreferredUpstream());
- when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
-
- // [2] WiFi connects but not validated/promoted to default -> mobile selected.
- final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
- wifiAgent.fakeConnect();
- assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
-
- // [3] WiFi validates and is promoted to the default network -> WiFi selected.
- mCM.makeDefaultNetwork(wifiAgent);
- assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
-
- // [4] DUN required, no other changes -> WiFi still selected
- mUNM.updateMobileRequiresDun(true);
- assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
-
- // [5] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
- mCM.makeDefaultNetwork(cellAgent);
- assertEquals(null, mUNM.getCurrentPreferredUpstream());
- // TODO: make sure that a DUN request has been filed. This is currently
- // triggered by code over in Tethering, but once that has been moved
- // into UNM we should test for this here.
-
- // [6] DUN network arrives -> DUN selected
- final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
- dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
- dunAgent.fakeConnect();
- assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
-
- // [7] Mobile is not permitted -> null selected
- when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
- assertEquals(null, mUNM.getCurrentPreferredUpstream());
- }
-
- @Test
- public void testLocalPrefixes() throws Exception {
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
- mUNM.startObserveAllNetworks();
-
- // [0] Test minimum set of local prefixes.
- Set<IpPrefix> local = mUNM.getLocalPrefixes();
- assertTrue(local.isEmpty());
-
- final Set<String> alreadySeen = new HashSet<>();
-
- // [1] Pretend Wi-Fi connects.
- final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
- final LinkProperties wifiLp = wifiAgent.linkProperties;
- wifiLp.setInterfaceName("wlan0");
- final String[] wifi_addrs = {
- "fe80::827a:bfff:fe6f:374d", "100.112.103.18",
- "2001:db8:4:fd00:827a:bfff:fe6f:374d",
- "2001:db8:4:fd00:6dea:325a:fdae:4ef4",
- "fd6a:a640:60bf:e985::123", // ULA address for good measure.
- };
- for (String addrStr : wifi_addrs) {
- final String cidr = addrStr.contains(":") ? "/64" : "/20";
- wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
- }
- wifiAgent.fakeConnect();
- wifiAgent.sendLinkProperties();
-
- local = mUNM.getLocalPrefixes();
- assertPrefixSet(local, INCLUDES, alreadySeen);
- final String[] wifiLinkPrefixes = {
- // Link-local prefixes are excluded and dealt with elsewhere.
- "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64",
- };
- assertPrefixSet(local, INCLUDES, wifiLinkPrefixes);
- Collections.addAll(alreadySeen, wifiLinkPrefixes);
- assertEquals(alreadySeen.size(), local.size());
-
- // [2] Pretend mobile connects.
- final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- final LinkProperties cellLp = cellAgent.linkProperties;
- cellLp.setInterfaceName("rmnet_data0");
- final String[] cell_addrs = {
- "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
- };
- for (String addrStr : cell_addrs) {
- final String cidr = addrStr.contains(":") ? "/64" : "/27";
- cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
- }
- cellAgent.fakeConnect();
- cellAgent.sendLinkProperties();
-
- local = mUNM.getLocalPrefixes();
- assertPrefixSet(local, INCLUDES, alreadySeen);
- final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" };
- assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
- Collections.addAll(alreadySeen, cellLinkPrefixes);
- assertEquals(alreadySeen.size(), local.size());
-
- // [3] Pretend DUN connects.
- final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
- dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
- final LinkProperties dunLp = dunAgent.linkProperties;
- dunLp.setInterfaceName("rmnet_data1");
- final String[] dun_addrs = {
- "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
- };
- for (String addrStr : dun_addrs) {
- final String cidr = addrStr.contains(":") ? "/64" : "/27";
- dunLp.addLinkAddress(new LinkAddress(addrStr + cidr));
- }
- dunAgent.fakeConnect();
- dunAgent.sendLinkProperties();
-
- local = mUNM.getLocalPrefixes();
- assertPrefixSet(local, INCLUDES, alreadySeen);
- final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" };
- assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
- Collections.addAll(alreadySeen, dunLinkPrefixes);
- assertEquals(alreadySeen.size(), local.size());
-
- // [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no
- // longer be included (should be properly removed).
- wifiAgent.fakeDisconnect();
- local = mUNM.getLocalPrefixes();
- assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
- assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
- assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
-
- // [5] Pretend mobile disconnected.
- cellAgent.fakeDisconnect();
- local = mUNM.getLocalPrefixes();
- assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
- assertPrefixSet(local, EXCLUDES, cellLinkPrefixes);
- assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
-
- // [6] Pretend DUN disconnected.
- dunAgent.fakeDisconnect();
- local = mUNM.getLocalPrefixes();
- assertTrue(local.isEmpty());
- }
-
- @Test
- public void testSelectMobileWhenMobileIsNotDefault() {
- final Collection<Integer> preferredTypes = new ArrayList<>();
- // Mobile has higher pirority than wifi.
- preferredTypes.add(TYPE_MOBILE_HIPRI);
- preferredTypes.add(TYPE_WIFI);
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
- mUNM.startObserveAllNetworks();
- // Setup wifi and make wifi as default network.
- final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
- wifiAgent.fakeConnect();
- mCM.makeDefaultNetwork(wifiAgent);
- // Setup mobile network.
- final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- cellAgent.fakeConnect();
-
- assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
- mUNM.selectPreferredUpstreamType(preferredTypes));
- verify(mEntitleMgr, times(1)).maybeRunProvisioning();
- }
-
- private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) {
- if (legacyType == TYPE_NONE) {
- assertTrue(ns == null);
- return;
- }
-
- final NetworkCapabilities nc =
- UpstreamNetworkMonitor.networkCapabilitiesForType(legacyType);
- assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities));
- }
-
- private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
- assertEquals(1, mCM.requested.size());
- assertEquals(1, mCM.legacyTypeMap.size());
- assertEquals(Integer.valueOf(upstreamType),
- mCM.legacyTypeMap.values().iterator().next());
- }
-
- private boolean isDunRequested() {
- for (NetworkRequest req : mCM.requested.values()) {
- if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
- return true;
- }
- }
- return false;
- }
-
- public static class TestConnectivityManager extends ConnectivityManager {
- public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
- public Set<NetworkCallback> trackingDefault = new HashSet<>();
- public TestNetworkAgent defaultNetwork = null;
- public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
- public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
- public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
-
- private int mNetworkId = 100;
-
- public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
- super(ctx, svc);
- }
-
- boolean hasNoCallbacks() {
- return allCallbacks.isEmpty()
- && trackingDefault.isEmpty()
- && listening.isEmpty()
- && requested.isEmpty()
- && legacyTypeMap.isEmpty();
- }
-
- boolean onlyHasDefaultCallbacks() {
- return (allCallbacks.size() == 1)
- && (trackingDefault.size() == 1)
- && listening.isEmpty()
- && requested.isEmpty()
- && legacyTypeMap.isEmpty();
- }
-
- boolean isListeningForAll() {
- final NetworkCapabilities empty = new NetworkCapabilities();
- empty.clearAll();
-
- for (NetworkRequest req : listening.values()) {
- if (req.networkCapabilities.equalRequestableCapabilities(empty)) {
- return true;
- }
- }
- return false;
- }
-
- int getNetworkId() {
- return ++mNetworkId;
- }
-
- void makeDefaultNetwork(TestNetworkAgent agent) {
- if (Objects.equals(defaultNetwork, agent)) return;
-
- final TestNetworkAgent formerDefault = defaultNetwork;
- defaultNetwork = agent;
-
- for (NetworkCallback cb : trackingDefault) {
- if (defaultNetwork != null) {
- cb.onAvailable(defaultNetwork.networkId);
- cb.onCapabilitiesChanged(
- defaultNetwork.networkId, defaultNetwork.networkCapabilities);
- cb.onLinkPropertiesChanged(
- defaultNetwork.networkId, defaultNetwork.linkProperties);
- }
- }
- }
-
- @Override
- public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
- assertFalse(allCallbacks.containsKey(cb));
- allCallbacks.put(cb, h);
- if (sDefaultRequest.equals(req)) {
- assertFalse(trackingDefault.contains(cb));
- trackingDefault.add(cb);
- } else {
- assertFalse(requested.containsKey(cb));
- requested.put(cb, req);
- }
- }
-
- @Override
- public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
- fail("Should never be called.");
- }
-
- @Override
- public void requestNetwork(NetworkRequest req,
- int timeoutMs, int legacyType, Handler h, NetworkCallback cb) {
- assertFalse(allCallbacks.containsKey(cb));
- allCallbacks.put(cb, h);
- assertFalse(requested.containsKey(cb));
- requested.put(cb, req);
- assertFalse(legacyTypeMap.containsKey(cb));
- if (legacyType != ConnectivityManager.TYPE_NONE) {
- legacyTypeMap.put(cb, legacyType);
- }
- }
-
- @Override
- public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) {
- assertFalse(allCallbacks.containsKey(cb));
- allCallbacks.put(cb, h);
- assertFalse(listening.containsKey(cb));
- listening.put(cb, req);
- }
-
- @Override
- public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
- fail("Should never be called.");
- }
-
- @Override
- public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
- fail("Should never be called.");
- }
-
- @Override
- public void registerDefaultNetworkCallback(NetworkCallback cb) {
- fail("Should never be called.");
- }
-
- @Override
- public void unregisterNetworkCallback(NetworkCallback cb) {
- if (trackingDefault.contains(cb)) {
- trackingDefault.remove(cb);
- } else if (listening.containsKey(cb)) {
- listening.remove(cb);
- } else if (requested.containsKey(cb)) {
- requested.remove(cb);
- legacyTypeMap.remove(cb);
- } else {
- fail("Unexpected callback removed");
- }
- allCallbacks.remove(cb);
-
- assertFalse(allCallbacks.containsKey(cb));
- assertFalse(trackingDefault.contains(cb));
- assertFalse(listening.containsKey(cb));
- assertFalse(requested.containsKey(cb));
- }
- }
-
- public static class TestNetworkAgent {
- public final TestConnectivityManager cm;
- public final Network networkId;
- public final int transportType;
- public final NetworkCapabilities networkCapabilities;
- public final LinkProperties linkProperties;
-
- public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
- this.cm = cm;
- this.networkId = new Network(cm.getNetworkId());
- this.transportType = transportType;
- networkCapabilities = new NetworkCapabilities();
- networkCapabilities.addTransportType(transportType);
- networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
- linkProperties = new LinkProperties();
- }
-
- public void fakeConnect() {
- for (NetworkCallback cb : cm.listening.keySet()) {
- cb.onAvailable(networkId);
- cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
- cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
- }
- }
-
- public void fakeDisconnect() {
- for (NetworkCallback cb : cm.listening.keySet()) {
- cb.onLost(networkId);
- }
- }
-
- public void sendLinkProperties() {
- for (NetworkCallback cb : cm.listening.keySet()) {
- cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
- }
- }
-
- @Override
- public String toString() {
- return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities);
- }
- }
-
- public static class TestStateMachine extends StateMachine {
- public final ArrayList<Message> messages = new ArrayList<>();
- private final State mLoggingState = new LoggingState();
-
- class LoggingState extends State {
- @Override public void enter() {
- messages.clear();
- }
-
- @Override public void exit() {
- messages.clear();
- }
-
- @Override public boolean processMessage(Message msg) {
- messages.add(msg);
- return true;
- }
- }
-
- public TestStateMachine() {
- super("UpstreamNetworkMonitor.TestStateMachine");
- addState(mLoggingState);
- setInitialState(mLoggingState);
- super.start();
- }
- }
-
- static NetworkCapabilities copy(NetworkCapabilities nc) {
- return new NetworkCapabilities(nc);
- }
-
- static LinkProperties copy(LinkProperties lp) {
- return new LinkProperties(lp);
- }
-
- static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
- final Set<String> expectedSet = new HashSet<>();
- Collections.addAll(expectedSet, expected);
- assertPrefixSet(prefixes, expectation, expectedSet);
- }
-
- static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected) {
- for (String expectedPrefix : expected) {
- final String errStr = expectation ? "did not find" : "found";
- assertEquals(
- String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix),
- expectation, prefixes.contains(new IpPrefix(expectedPrefix)));
- }
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 92b8608f4f6c..bd26d44bed6f 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -25,6 +25,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
+import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -296,11 +297,29 @@ final class RemoteAugmentedAutofillService
dataset.getId(), clientState);
try {
final ArrayList<AutofillId> fieldIds = dataset.getFieldIds();
- final int size = fieldIds.size();
- final boolean hideHighlight = size == 1
- && fieldIds.get(0).equals(focusedId);
- client.autofill(sessionId, fieldIds, dataset.getFieldValues(),
- hideHighlight);
+ final ClipData content = dataset.getFieldContent();
+ if (content != null) {
+ final AutofillId fieldId = fieldIds.get(0);
+ if (sDebug) {
+ Slog.d(TAG, "Calling client autofillContent(): "
+ + "id=" + fieldId + ", content=" + content);
+ }
+ client.autofillContent(sessionId, fieldId, content);
+ } else {
+ final int size = fieldIds.size();
+ final boolean hideHighlight = size == 1
+ && fieldIds.get(0).equals(focusedId);
+ if (sDebug) {
+ Slog.d(TAG, "Calling client autofill(): "
+ + "ids=" + fieldIds
+ + ", values=" + dataset.getFieldValues());
+ }
+ client.autofill(
+ sessionId,
+ fieldIds,
+ dataset.getFieldValues(),
+ hideHighlight);
+ }
inlineSuggestionsCallback.apply(
InlineFillUi.emptyUi(focusedId));
} catch (RemoteException e) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f596b072d713..0302b2251f10 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -47,6 +47,7 @@ import android.app.IAssistDataReceiver;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.AutofillOverlay;
import android.app.assist.AssistStructure.ViewNode;
+import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -1493,11 +1494,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.d(TAG, "Auth result for augmented autofill: sessionId=" + id
+ ", authId=" + authId + ", dataset=" + dataset);
}
- if (dataset == null
- || dataset.getFieldIds().size() != 1
- || dataset.getFieldIds().get(0) == null
- || dataset.getFieldValues().size() != 1
- || dataset.getFieldValues().get(0) == null) {
+ final AutofillId fieldId = (dataset != null && dataset.getFieldIds().size() == 1)
+ ? dataset.getFieldIds().get(0) : null;
+ final AutofillValue value = (dataset != null && dataset.getFieldValues().size() == 1)
+ ? dataset.getFieldValues().get(0) : null;
+ final ClipData content = (dataset != null) ? dataset.getFieldContent() : null;
+ if (fieldId == null || (value == null && content == null)) {
if (sDebug) {
Slog.d(TAG, "Rejecting empty/invalid auth result");
}
@@ -1505,10 +1507,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
removeSelfLocked();
return;
}
- final List<AutofillId> fieldIds = dataset.getFieldIds();
- final List<AutofillValue> autofillValues = dataset.getFieldValues();
- final AutofillId fieldId = fieldIds.get(0);
- final AutofillValue value = autofillValues.get(0);
// Update state to ensure that after filling the field here we don't end up firing another
// autofill request that will end up showing the same suggestions to the user again. When
@@ -1524,13 +1522,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Fill the value into the field.
if (sDebug) {
- Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value);
+ Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value
+ + ", content=" + content);
}
try {
- mClient.autofill(id, fieldIds, autofillValues, true);
+ if (content != null) {
+ mClient.autofillContent(id, fieldId, content);
+ } else {
+ mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues(), true);
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Error filling after auth: fieldId=" + fieldId + ", value=" + value
- + ", error=" + e);
+ + ", content=" + content, e);
}
// Clear the suggestions since the user already accepted one of them.
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 032820dc97f8..e32324941aef 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -309,6 +309,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
mFindDeviceCallback = callback;
mRequest = request;
mCallingPackage = callingPackage;
+ request.setCallingPackage(callingPackage);
callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0);
final long callingIdentity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 85d77f246bbb..21ad6de045bb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2431,9 +2431,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
try {
if (VDBG || DDBG) log("Setting MTU size: " + iface + ", " + mtu);
- mNMS.setMtu(iface, mtu);
- } catch (Exception e) {
- Slog.e(TAG, "exception in setMtu()" + e);
+ mNetd.interfaceSetMtu(iface, mtu);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "exception in interfaceSetMtu()" + e);
}
}
@@ -6078,7 +6078,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (final String iface : interfaceDiff.added) {
try {
if (DBG) log("Adding iface " + iface + " to network " + netId);
- mNMS.addInterfaceToNetwork(iface, netId);
+ mNetd.networkAddInterface(netId, iface);
wakeupModifyInterface(iface, caps, true);
bs.noteNetworkInterfaceType(iface, legacyType);
} catch (Exception e) {
@@ -6090,7 +6090,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
wakeupModifyInterface(iface, caps, false);
- mNMS.removeInterfaceFromNetwork(iface, netId);
+ mNetd.networkRemoveInterface(netId, iface);
} catch (Exception e) {
loge("Exception removing interface: " + e);
}
@@ -6256,9 +6256,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
final int newPermission = getNetworkPermission(newNc);
if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
try {
- mNMS.setNetworkPermission(nai.network.netId, newPermission);
- } catch (RemoteException e) {
- loge("Exception in setNetworkPermission: " + e);
+ mNetd.networkSetPermissionForNetwork(nai.network.netId, newPermission);
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("Exception in networkSetPermissionForNetwork: " + e);
}
}
}
@@ -6700,11 +6700,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
try {
if (null != newNetwork) {
- mNMS.setDefaultNetId(newNetwork.network.netId);
+ mNetd.networkSetDefault(newNetwork.network.netId);
} else {
- mNMS.clearDefaultNetId();
+ mNetd.networkClearDefault();
}
- } catch (Exception e) {
+ } catch (RemoteException | ServiceSpecificException e) {
loge("Exception setting default network :" + e);
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index ea14fadff433..1c99465dfebf 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -945,17 +945,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
@Override
- public void setMtu(String iface, int mtu) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- try {
- mNetdService.interfaceSetMtu(iface, mtu);
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
public void shutdown() {
// TODO: remove from aidl if nobody calls externally
mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
@@ -1985,16 +1974,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
pw.println("]");
}
- @Override
- public void addInterfaceToNetwork(String iface, int netId) {
- modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, netId, iface);
- }
-
- @Override
- public void removeInterfaceFromNetwork(String iface, int netId) {
- modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, netId, iface);
- }
-
private void modifyInterfaceInNetwork(boolean add, int netId, String iface) {
NetworkStack.checkNetworkStackPermission(mContext);
try {
@@ -2030,39 +2009,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
@Override
- public void setDefaultNetId(int netId) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- try {
- mNetdService.networkSetDefault(netId);
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public void clearDefaultNetId() {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- try {
- mNetdService.networkClearDefault();
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public void setNetworkPermission(int netId, int permission) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- try {
- mNetdService.networkSetPermissionForNetwork(netId, permission);
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
public void allowProtect(int uid) {
NetworkStack.checkNetworkStackPermission(mContext);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 0135b9d2f8ab..7051452f25cc 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -501,7 +501,7 @@ final class UiModeManagerService extends SystemService {
}
/**
- * Updates the night mode setting in Settings.Global and returns if the value was successfully
+ * Updates the night mode setting in Settings.Secure and returns if the value was successfully
* changed.
*
* @param context A valid context
@@ -516,7 +516,8 @@ final class UiModeManagerService extends SystemService {
int oldNightMode = mNightMode;
if (mSetupWizardComplete) {
mNightMode = Secure.getIntForUser(context.getContentResolver(),
- Secure.UI_NIGHT_MODE, mNightMode, userId);
+ Secure.UI_NIGHT_MODE, res.getInteger(
+ com.android.internal.R.integer.config_defaultNightMode), userId);
mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0;
mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(),
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
new file mode 100644
index 000000000000..db7e16ca8b25
--- /dev/null
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.vcn.IVcnManagementService;
+
+/**
+ * VcnManagementService manages Virtual Carrier Network profiles and lifecycles.
+ *
+ * <pre>The internal structure of the VCN Management subsystem is as follows:
+ *
+ * +------------------------+ 1:1 +--------------------------------+
+ * | VcnManagementService | ------------ Creates -------------> | TelephonySubscriptionManager |
+ * | | | |
+ * | Manages configs and | | Tracks subscriptions, carrier |
+ * | VcnInstance lifecycles | <--- Notifies of subscription & --- | privilege changes, caches maps |
+ * +------------------------+ carrier privilege changes +--------------------------------+
+ * | 1:N ^
+ * | |
+ * | +-------------------------------+
+ * +---------------+ |
+ * | |
+ * Creates when config present, |
+ * subscription group active, and |
+ * providing app is carrier privileged Notifies of safe
+ * | mode state changes
+ * v |
+ * +-----------------------------------------------------------------------+
+ * | VcnInstance |
+ * | |
+ * | Manages tunnel lifecycles based on fulfillable NetworkRequest(s) |
+ * | and overall safe-mode |
+ * +-----------------------------------------------------------------------+
+ * | 1:N ^
+ * Creates to fulfill |
+ * NetworkRequest(s), tears Notifies of VcnTunnel
+ * down when no longer needed teardown (e.g. Network reaped)
+ * | and safe-mode timer changes
+ * v |
+ * +-----------------------------------------------------------------------+
+ * | VcnTunnel |
+ * | |
+ * | Manages a single (IKEv2) tunnel session and NetworkAgent, |
+ * | handles mobility events, (IPsec) Tunnel setup and safe-mode timers |
+ * +-----------------------------------------------------------------------+
+ * | 1:1 ^
+ * | |
+ * Creates upon instantiation Notifies of changes in
+ * | selected underlying network
+ * | or its properties
+ * v |
+ * +-----------------------------------------------------------------------+
+ * | UnderlyingNetworkTracker |
+ * | |
+ * | Manages lifecycle of underlying physical networks, filing requests to |
+ * | bring them up, and releasing them as they become no longer necessary |
+ * +-----------------------------------------------------------------------+
+ * </pre>
+ *
+ * @hide
+ */
+public class VcnManagementService extends IVcnManagementService.Stub {
+ @NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
+
+ public static final boolean VDBG = false; // STOPSHIP: if true
+
+ /* Binder context for this service */
+ @NonNull private final Context mContext;
+ @NonNull private final Dependencies mDeps;
+
+ private VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
+ mContext = requireNonNull(context, "Missing context");
+ mDeps = requireNonNull(deps, "Missing dependencies");
+ }
+
+ // Package-visibility for SystemServer to create instances.
+ static VcnManagementService create(@NonNull Context context) {
+ return new VcnManagementService(context, new Dependencies());
+ }
+
+ private static class Dependencies {}
+
+ /** Notifies the VcnManagementService that external dependencies can be set up */
+ public void systemReady() {}
+}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index afddd650c46c..74fba0441be8 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1794,25 +1794,20 @@ public class VibratorService extends IVibratorService.Stub
mWakeLock.setWorkSource(mTmpWorkSource);
}
- private long delayLocked(long duration) {
+ private void delayLocked(long wakeUpTime) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked");
try {
- long durationRemaining = duration;
- if (duration > 0) {
- final long bedtime = duration + SystemClock.uptimeMillis();
- do {
- try {
- this.wait(durationRemaining);
- }
- catch (InterruptedException e) { }
- if (mForceStop) {
- break;
- }
- durationRemaining = bedtime - SystemClock.uptimeMillis();
- } while (durationRemaining > 0);
- return duration - durationRemaining;
+ long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
+ while (durationRemaining > 0) {
+ try {
+ this.wait(durationRemaining);
+ } catch (InterruptedException e) {
+ }
+ if (mForceStop) {
+ break;
+ }
+ durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
}
- return 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
@@ -1846,7 +1841,8 @@ public class VibratorService extends IVibratorService.Stub
final int repeat = mWaveform.getRepeatIndex();
int index = 0;
- long onDuration = 0;
+ long nextStepStartTime = SystemClock.uptimeMillis();
+ long nextVibratorStopTime = 0;
while (!mForceStop) {
if (index < len) {
final int amplitude = amplitudes[index];
@@ -1855,27 +1851,33 @@ public class VibratorService extends IVibratorService.Stub
continue;
}
if (amplitude != 0) {
- if (onDuration <= 0) {
+ long now = SystemClock.uptimeMillis();
+ if (nextVibratorStopTime <= now) {
// Telling the vibrator to start multiple times usually causes
// effects to feel "choppy" because the motor resets at every on
// command. Instead we figure out how long our next "on" period
// is going to be, tell the motor to stay on for the full
// duration, and then wake up to change the amplitude at the
// appropriate intervals.
- onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
- repeat);
+ long onDuration = getTotalOnDuration(
+ timings, amplitudes, index - 1, repeat);
mVibration.effect = VibrationEffect.createOneShot(
onDuration, amplitude);
doVibratorOn(mVibration);
+ nextVibratorStopTime = now + onDuration;
} else {
+ // Vibrator is already ON, so just change its amplitude.
doVibratorSetAmplitude(amplitude);
}
}
- long waitTime = delayLocked(duration);
- if (amplitude != 0) {
- onDuration -= waitTime;
- }
+ // We wait until the time this waveform step was supposed to end,
+ // calculated from the time it was supposed to start. All start times
+ // are calculated from the waveform original start time by adding the
+ // input durations. Any scheduling or processing delay should not affect
+ // this step's perceived total duration. They will be amortized here.
+ nextStepStartTime += duration;
+ delayLocked(nextStepStartTime);
} else if (repeat < 0) {
break;
} else {
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 5219df4a841d..4b59112b3524 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
@@ -43,7 +43,7 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
throws RemoteException {
mFingerprintService.prepareForAuthentication(token, operationId, userId, sensorReceiver,
- opPackageName, cookie, callingUid, callingPid, callingUserId, null /* surface */);
+ opPackageName, cookie, callingUid, callingPid, callingUserId);
}
@Override
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 9ac12ed11ded..c88247f59871 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
@@ -46,7 +46,6 @@ import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
-import android.os.NativeHandle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -56,7 +55,6 @@ import android.util.EventLog;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.Surface;
import com.android.internal.R;
import com.android.internal.util.DumpUtils;
@@ -156,8 +154,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId,
- final IFingerprintServiceReceiver receiver, final String opPackageName,
- Surface surface) {
+ final IFingerprintServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -167,7 +164,7 @@ public class FingerprintService extends SystemService {
}
provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
- receiver, opPackageName, surface);
+ receiver, opPackageName);
}
@Override // Binder call
@@ -185,8 +182,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void authenticate(final IBinder token, final long operationId, final int userId,
- final IFingerprintServiceReceiver receiver, final String opPackageName,
- final Surface surface) {
+ final IFingerprintServiceReceiver receiver, final String opPackageName) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -233,8 +229,7 @@ public class FingerprintService extends SystemService {
@Override
public void detectFingerprint(final IBinder token, final int userId,
- final IFingerprintServiceReceiver receiver, final String opPackageName,
- final Surface surface) {
+ final IFingerprintServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
if (!Utils.isKeyguard(getContext(), opPackageName)) {
Slog.w(TAG, "detectFingerprint called from non-sysui package: " + opPackageName);
@@ -255,15 +250,14 @@ public class FingerprintService extends SystemService {
}
provider.second.scheduleFingerDetect(provider.first, token, userId,
- new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
+ new ClientMonitorCallbackConverter(receiver), opPackageName,
BiometricsProtoEnums.CLIENT_KEYGUARD);
}
@Override // Binder call
public void prepareForAuthentication(IBinder token, long operationId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName,
- int cookie, int callingUid, int callingPid, int callingUserId,
- Surface surface) {
+ int cookie, int callingUid, int callingPid, int callingUserId) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -729,6 +723,4 @@ public class FingerprintService extends SystemService {
}
return appOpsOk;
}
-
- private native NativeHandle convertSurfaceToNativeHandle(Surface surface);
}
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 1ed66a247bd0..c13a6569b16a 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,15 +18,14 @@ package com.android.server.biometrics.sensors.fingerprint;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.fingerprint.Fingerprint;
import android.hardware.biometrics.ITestSession;
+import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.util.proto.ProtoOutputStream;
-import android.view.Surface;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -62,7 +61,8 @@ public interface ServiceProvider {
*/
boolean containsSensor(int sensorId);
- @NonNull List<FingerprintSensorPropertiesInternal> getSensorProperties();
+ @NonNull
+ List<FingerprintSensorPropertiesInternal> getSensorProperties();
void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
@@ -73,14 +73,13 @@ public interface ServiceProvider {
@NonNull String opPackageName, long challenge);
void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
- @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @Nullable Surface surface);
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName);
void cancelEnrollment(int sensorId, @NonNull IBinder token);
void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
- @Nullable Surface surface, int statsClient);
+ int statsClient);
void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@@ -100,9 +99,11 @@ public interface ServiceProvider {
void rename(int sensorId, int fingerId, int userId, @NonNull String name);
- @NonNull List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId);
+ @NonNull
+ List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId);
- @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId);
+ @LockoutTracker.LockoutMode
+ int getLockoutModeForUser(int sensorId, int userId);
long getAuthenticatorId(int sensorId, int userId);
@@ -118,5 +119,6 @@ public interface ServiceProvider {
void dumpInternal(int sensorId, @NonNull PrintWriter pw);
- @NonNull ITestSession createTestSession(int sensorId, @NonNull String opPackageName);
+ @NonNull
+ ITestSession createTestSession(int sensorId, @NonNull String opPackageName);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 6bb40e6630bf..973132533661 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -121,7 +121,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), null /* surface */);
+ mContext.getOpPackageName());
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 4d07f583fdf5..0ebfe9381d15 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -40,7 +40,6 @@ import android.os.UserManager;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Surface;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
@@ -341,7 +340,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Override
public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
- @NonNull String opPackageName, @Nullable Surface surface) {
+ @NonNull String opPackageName) {
mHandler.post(() -> {
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
@@ -387,7 +386,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Override
public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
- @Nullable Surface surface, int statsClient) {
+ int statsClient) {
mHandler.post(() -> {
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index e0ea99077d51..65ce34d31b61 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -122,7 +122,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), null /* surface */);
+ mContext.getOpPackageName());
}
@Override
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 241c911ce9ab..a806e3f61bc8 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
@@ -46,7 +46,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.Surface;
import com.android.internal.R;
import com.android.internal.util.FrameworkStatsLog;
@@ -539,8 +538,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@Override
public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
- @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @Nullable Surface surface) {
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -571,7 +569,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@Override
public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
- @Nullable Surface surface, int statsClient) {
+ int statsClient) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 4c2b1e34fa78..aebcdf19ecf4 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -201,6 +201,7 @@ public class Vpn {
private final Context mContext;
@VisibleForTesting final Dependencies mDeps;
private final NetworkInfo mNetworkInfo;
+ private int mLegacyState;
@VisibleForTesting protected String mPackage;
private int mOwnerUID;
private boolean mIsPackageTargetingAtLeastQ;
@@ -415,6 +416,7 @@ public class Vpn {
Log.wtf(TAG, "Problem registering observer", e);
}
+ mLegacyState = LegacyVpnInfo.STATE_DISCONNECTED;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE,
"" /* subtypeName */);
mNetworkCapabilities = new NetworkCapabilities();
@@ -440,6 +442,7 @@ public class Vpn {
@VisibleForTesting
protected void updateState(DetailedState detailedState, String reason) {
if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
+ mLegacyState = LegacyVpnInfo.stateFromNetworkInfo(detailedState);
mNetworkInfo.setDetailedState(detailedState, reason, null);
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -1243,6 +1246,7 @@ public class Vpn {
// behaves the same as when it uses the default network.
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ mLegacyState = LegacyVpnInfo.STATE_CONNECTING;
mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
@@ -2265,7 +2269,7 @@ public class Vpn {
final LegacyVpnInfo info = new LegacyVpnInfo();
info.key = mConfig.user;
- info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
+ info.state = mLegacyState;
if (mNetworkInfo.isConnected()) {
info.intent = mStatusIntent;
}
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 73f788901dc9..5e1df27167c2 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.wm.utils.RotationAnimationUtils.hasProtectedContent;
+
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.display.DisplayManagerInternal;
@@ -35,7 +37,6 @@ import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceSession;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
@@ -75,6 +76,7 @@ final class ColorFade {
private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
+ private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0;
private final int mDisplayId;
@@ -87,7 +89,6 @@ final class ColorFade {
private int mDisplayLayerStack; // layer stack associated with primary display
private int mDisplayWidth; // real width, not rotated
private int mDisplayHeight; // real height, not rotated
- private SurfaceSession mSurfaceSession;
private SurfaceControl mSurfaceControl;
private Surface mSurface;
private NaturalSurfaceLayout mSurfaceLayout;
@@ -97,7 +98,8 @@ final class ColorFade {
private EGLSurface mEglSurface;
private boolean mSurfaceVisible;
private float mSurfaceAlpha;
- private boolean mIsWideColor;
+ private boolean mLastWasWideColor;
+ private boolean mLastWasProtectedContent;
// Texture names. We only use one texture, which contains the screenshot.
private final int[] mTexNames = new int[1];
@@ -157,9 +159,28 @@ final class ColorFade {
mDisplayWidth = displayInfo.getNaturalWidth();
mDisplayHeight = displayInfo.getNaturalHeight();
- // Prepare the surface for drawing.
- if (!(createSurface() && createEglContext() && createEglSurface() &&
- captureScreenshotTextureAndSetViewport())) {
+ final IBinder token = SurfaceControl.getInternalDisplayToken();
+ if (token == null) {
+ Slog.e(TAG,
+ "Failed to take screenshot because internal display is disconnected");
+ return false;
+ }
+ boolean isWideColor = SurfaceControl.getActiveColorMode(token)
+ == Display.COLOR_MODE_DISPLAY_P3;
+
+ // Set mPrepared here so if initialization fails, resources can be cleaned up.
+ mPrepared = true;
+
+ SurfaceControl.ScreenshotHardwareBuffer hardwareBuffer = captureScreen();
+ if (hardwareBuffer == null) {
+ dismiss();
+ return false;
+ }
+
+ boolean isProtected = hasProtectedContent(hardwareBuffer.getHardwareBuffer());
+ if (!(createSurfaceControl(hardwareBuffer.containsSecureLayers())
+ && createEglContext(isProtected) && createEglSurface(isProtected, isWideColor)
+ && setScreenshotTextureAndSetViewport(hardwareBuffer))) {
dismiss();
return false;
}
@@ -180,7 +201,8 @@ final class ColorFade {
// Done.
mCreatedResources = true;
- mPrepared = true;
+ mLastWasProtectedContent = isProtected;
+ mLastWasWideColor = isWideColor;
// Dejanking optimization.
// Some GL drivers can introduce a lot of lag in the first few frames as they
@@ -466,7 +488,8 @@ final class ColorFade {
mProjMatrix[15] = 1f;
}
- private boolean captureScreenshotTextureAndSetViewport() {
+ private boolean setScreenshotTextureAndSetViewport(
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) {
if (!attachEglContext()) {
return false;
}
@@ -482,27 +505,9 @@ final class ColorFade {
final SurfaceTexture st = new SurfaceTexture(mTexNames[0]);
final Surface s = new Surface(st);
try {
- final IBinder token = SurfaceControl.getInternalDisplayToken();
- if (token == null) {
- Slog.e(TAG,
- "Failed to take screenshot because internal display is disconnected");
- return false;
- }
-
- mIsWideColor = SurfaceControl.getActiveColorMode(token)
- == Display.COLOR_MODE_DISPLAY_P3;
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- mDisplayManagerInternal.systemScreenshot(mDisplayId);
- if (screenshotBuffer == null) {
- Slog.e(TAG, "Failed to take screenshot. Buffer is null");
- return false;
- }
s.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
screenshotBuffer.getColorSpace());
- if (screenshotBuffer.containsSecureLayers()) {
- mTransaction.setSecure(mSurfaceControl, true).apply();
- }
st.updateTexImage();
st.getTransformMatrix(mTexMatrix);
} finally {
@@ -535,7 +540,52 @@ final class ColorFade {
}
}
- private boolean createEglContext() {
+ private SurfaceControl.ScreenshotHardwareBuffer captureScreen() {
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ mDisplayManagerInternal.systemScreenshot(mDisplayId);
+ if (screenshotBuffer == null) {
+ Slog.e(TAG, "Failed to take screenshot. Buffer is null");
+ return null;
+ }
+ return screenshotBuffer;
+ }
+
+ private boolean createSurfaceControl(boolean isSecure) {
+ if (mSurfaceControl != null) {
+ mTransaction.setSecure(mSurfaceControl, isSecure).apply();
+ return true;
+ }
+
+ try {
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder()
+ .setName("ColorFade")
+ .setSecure(isSecure)
+ .setCallsite("ColorFade.createSurface");
+ if (mMode == MODE_FADE) {
+ builder.setColorLayer();
+ } else {
+ builder.setBufferSize(mDisplayWidth, mDisplayHeight);
+ }
+ mSurfaceControl = builder.build();
+ } catch (OutOfResourcesException ex) {
+ Slog.e(TAG, "Unable to create surface.", ex);
+ return false;
+ }
+
+ mTransaction.setLayerStack(mSurfaceControl, mDisplayLayerStack);
+ mTransaction.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, mDisplayId,
+ mSurfaceControl);
+ mSurfaceLayout.onDisplayTransaction(mTransaction);
+ mTransaction.apply();
+
+ mSurface = new Surface();
+ mSurface.copyFrom(mSurfaceControl);
+
+ return true;
+ }
+
+ private boolean createEglContext(boolean isProtected) {
if (mEglDisplay == null) {
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
@@ -576,13 +626,25 @@ final class ColorFade {
mEglConfig = eglConfigs[0];
}
+ // The old context needs to be destroyed if the protected flag has changed. The context will
+ // be recreated based on the protected flag
+ if (mEglContext != null && isProtected != mLastWasProtectedContent) {
+ EGL14.eglDestroyContext(mEglDisplay, mEglContext);
+ mEglContext = null;
+ }
+
if (mEglContext == null) {
int[] eglContextAttribList = new int[] {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL14.EGL_NONE, EGL14.EGL_NONE,
EGL14.EGL_NONE
};
- mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
- EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
+ if (isProtected) {
+ eglContextAttribList[2] = EGL_PROTECTED_CONTENT_EXT;
+ eglContextAttribList[3] = EGL14.EGL_TRUE;
+ }
+ mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
+ eglContextAttribList, 0);
if (mEglContext == null) {
logEglError("eglCreateContext");
return false;
@@ -591,53 +653,34 @@ final class ColorFade {
return true;
}
- private boolean createSurface() {
- if (mSurfaceSession == null) {
- mSurfaceSession = new SurfaceSession();
- }
-
- if (mSurfaceControl == null) {
- try {
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession)
- .setName("ColorFade")
- .setCallsite("ColorFade.createSurface");
- if (mMode == MODE_FADE) {
- builder.setColorLayer();
- } else {
- builder.setBufferSize(mDisplayWidth, mDisplayHeight);
- }
- mSurfaceControl = builder.build();
- } catch (OutOfResourcesException ex) {
- Slog.e(TAG, "Unable to create surface.", ex);
- return false;
- }
-
- mTransaction.setLayerStack(mSurfaceControl, mDisplayLayerStack);
- mTransaction.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
- mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
- mDisplayId, mSurfaceControl);
- mSurfaceLayout.onDisplayTransaction(mTransaction);
- mTransaction.apply();
-
- mSurface = new Surface();
- mSurface.copyFrom(mSurfaceControl);
-
+ private boolean createEglSurface(boolean isProtected, boolean isWideColor) {
+ // The old surface needs to be destroyed if either the protected flag or wide color flag has
+ // changed. The surface will be recreated based on the new flags.
+ boolean didContentAttributesChange =
+ isProtected != mLastWasProtectedContent || isWideColor != mLastWasWideColor;
+ if (mEglSurface != null && didContentAttributesChange) {
+ EGL14.eglDestroySurface(mEglDisplay, mEglSurface);
+ mEglSurface = null;
}
- return true;
- }
- private boolean createEglSurface() {
if (mEglSurface == null) {
int[] eglSurfaceAttribList = new int[] {
EGL14.EGL_NONE,
EGL14.EGL_NONE,
+ EGL14.EGL_NONE,
+ EGL14.EGL_NONE,
EGL14.EGL_NONE
};
+ int index = 0;
// If the current display is in wide color, then so is the screenshot.
- if (mIsWideColor) {
- eglSurfaceAttribList[0] = EGL_GL_COLORSPACE_KHR;
- eglSurfaceAttribList[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+ if (isWideColor) {
+ eglSurfaceAttribList[index++] = EGL_GL_COLORSPACE_KHR;
+ eglSurfaceAttribList[index++] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+ }
+ if (isProtected) {
+ eglSurfaceAttribList[index++] = EGL_PROTECTED_CONTENT_EXT;
+ eglSurfaceAttribList[index] = EGL14.EGL_TRUE;
}
// turn our SurfaceControl into a Surface
mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c4dfcf5c3165..6a3e7b7a0ecd 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1293,6 +1293,7 @@ public final class DisplayManagerService extends SystemService {
.setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight())
.setUseIdentityTransform(true)
.setCaptureSecureLayers(true)
+ .setAllowProtected(true)
.build();
return SurfaceControl.captureDisplay(captureArgs);
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 71b377cc2f11..c013145b5269 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -50,11 +51,14 @@ import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+
/**
* The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
* picked by the system based on system-wide and display-specific configuration.
@@ -99,6 +103,29 @@ public class DisplayModeDirector {
private boolean mAlwaysRespectAppRequest;
+ @IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
+ SWITCHING_TYPE_NONE,
+ SWITCHING_TYPE_WITHIN_GROUPS,
+ SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SwitchingType {}
+
+ // No mode switching will happen.
+ public static final int SWITCHING_TYPE_NONE = 0;
+ // Allow only refresh rate switching between modes in the same configuration group. This way
+ // only switches without visual interruptions for the user will be allowed.
+ public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1;
+ // Allow refresh rate switching between all refresh rates even if the switch with have visual
+ // interruptions for the user.
+ public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2;
+
+ /**
+ * The allowed refresh rate switching type. This is used by SurfaceFlinger.
+ */
+ @SwitchingType
+ private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS;
+
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
@@ -290,14 +317,37 @@ public class DisplayModeDirector {
appRequestSummary.maxRefreshRate));
}
+ // If the application requests a given mode with preferredModeId function, it will be
+ // stored as baseModeId.
int baseModeId = defaultMode.getModeId();
if (availableModes.length > 0) {
baseModeId = availableModes[0];
}
- // filterModes function is going to filter the modes based on the voting system. If
- // the application requests a given mode with preferredModeId function, it will be
- // stored as baseModeId.
+ if (mModeSwitchingType == SWITCHING_TYPE_NONE) {
+ Display.Mode baseMode = null;
+ for (Display.Mode mode : modes) {
+ if (mode.getModeId() == baseModeId) {
+ baseMode = mode;
+ break;
+ }
+ }
+ if (baseMode == null) {
+ // This should never happen.
+ throw new IllegalStateException(
+ "The base mode with id " + baseModeId
+ + " is not in the list of supported modes.");
+ }
+ float fps = baseMode.getRefreshRate();
+ return new DesiredDisplayModeSpecs(baseModeId,
+ /*allowGroupSwitching */ false,
+ new RefreshRateRange(fps, fps),
+ new RefreshRateRange(fps, fps));
+ }
+
+ boolean allowGroupSwitching =
+ mModeSwitchingType == SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
return new DesiredDisplayModeSpecs(baseModeId,
+ allowGroupSwitching,
new RefreshRateRange(
primarySummary.minRefreshRate, primarySummary.maxRefreshRate),
new RefreshRateRange(
@@ -385,6 +435,26 @@ public class DisplayModeDirector {
}
/**
+ * Sets the display mode switching type.
+ * @param type
+ */
+ public void setModeSwitchingType(@SwitchingType int type) {
+ synchronized (mLock) {
+ mModeSwitchingType = type;
+ }
+ }
+
+ /**
+ * Returns the display mode switching type.
+ */
+ @SwitchingType
+ public int getModeSwitchingType() {
+ synchronized (mLock) {
+ return mModeSwitchingType;
+ }
+ }
+
+ /**
* Print the object's state and debug information into the given stream.
*
* @param pw The stream to dump information to.
@@ -416,6 +486,7 @@ public class DisplayModeDirector {
pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
}
}
+ pw.println(" mModeSwitchingType: " + switchingTypeToString(mModeSwitchingType));
pw.println(" mAlwaysRespectAppRequest: " + mAlwaysRespectAppRequest);
mSettingsObserver.dumpLocked(pw);
mAppRequestObserver.dumpLocked(pw);
@@ -472,7 +543,6 @@ public class DisplayModeDirector {
}
private SparseArray<Vote> getOrCreateVotesByDisplay(int displayId) {
- int index = mVotesByDisplay.indexOfKey(displayId);
if (mVotesByDisplay.indexOfKey(displayId) >= 0) {
return mVotesByDisplay.get(displayId);
} else {
@@ -482,6 +552,19 @@ public class DisplayModeDirector {
}
}
+ private static String switchingTypeToString(@SwitchingType int type) {
+ switch (type) {
+ case SWITCHING_TYPE_NONE:
+ return "SWITCHING_TYPE_NONE";
+ case SWITCHING_TYPE_WITHIN_GROUPS:
+ return "SWITCHING_TYPE_WITHIN_GROUPS";
+ case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
+ return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS";
+ default:
+ return "Unknown SwitchingType " + type;
+ }
+ }
+
@VisibleForTesting
void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) {
mSupportedModesByDisplay = supportedModesByDisplay;
@@ -631,18 +714,26 @@ public class DisplayModeDirector {
* SurfaceControl.DesiredDisplayConfigSpecs uses, and the mode ID used here.
*/
public static final class DesiredDisplayModeSpecs {
+
/**
* Base mode ID. This is what system defaults to for all other settings, or
* if the refresh rate range is not available.
*/
public int baseModeId;
+
+ /**
+ * If true this will allow switching between modes in different display configuration
+ * groups. This way the user may see visual interruptions when the display mode changes.
+ */
+ public boolean allowGroupSwitching;
+
/**
* The primary refresh rate range.
*/
public final RefreshRateRange primaryRefreshRateRange;
/**
* The app request refresh rate range. Lower priority considerations won't be included in
- * this range, allowing surface flinger to consider additional refresh rates for apps that
+ * this range, allowing SurfaceFlinger to consider additional refresh rates for apps that
* call setFrameRate(). This range will be greater than or equal to the primary refresh rate
* range, never smaller.
*/
@@ -654,9 +745,11 @@ public class DisplayModeDirector {
}
public DesiredDisplayModeSpecs(int baseModeId,
+ boolean allowGroupSwitching,
@NonNull RefreshRateRange primaryRefreshRateRange,
@NonNull RefreshRateRange appRequestRefreshRateRange) {
this.baseModeId = baseModeId;
+ this.allowGroupSwitching = allowGroupSwitching;
this.primaryRefreshRateRange = primaryRefreshRateRange;
this.appRequestRefreshRateRange = appRequestRefreshRateRange;
}
@@ -666,10 +759,12 @@ public class DisplayModeDirector {
*/
@Override
public String toString() {
- return String.format("baseModeId=%d primaryRefreshRateRange=[%.0f %.0f]"
+ return String.format("baseModeId=%d allowGroupSwitching=%b"
+ + " primaryRefreshRateRange=[%.0f %.0f]"
+ " appRequestRefreshRateRange=[%.0f %.0f]",
- baseModeId, primaryRefreshRateRange.min, primaryRefreshRateRange.max,
- appRequestRefreshRateRange.min, appRequestRefreshRateRange.max);
+ baseModeId, allowGroupSwitching, primaryRefreshRateRange.min,
+ primaryRefreshRateRange.max, appRequestRefreshRateRange.min,
+ appRequestRefreshRateRange.max);
}
/**
* Checks whether the two objects have the same values.
@@ -689,6 +784,9 @@ public class DisplayModeDirector {
if (baseModeId != desiredDisplayModeSpecs.baseModeId) {
return false;
}
+ if (allowGroupSwitching != desiredDisplayModeSpecs.allowGroupSwitching) {
+ return false;
+ }
if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) {
return false;
}
@@ -701,7 +799,8 @@ public class DisplayModeDirector {
@Override
public int hashCode() {
- return Objects.hash(baseModeId, primaryRefreshRateRange, appRequestRefreshRateRange);
+ return Objects.hash(baseModeId, allowGroupSwitching, primaryRefreshRateRange,
+ appRequestRefreshRateRange);
}
/**
@@ -709,6 +808,7 @@ public class DisplayModeDirector {
*/
public void copyFrom(DesiredDisplayModeSpecs other) {
baseModeId = other.baseModeId;
+ allowGroupSwitching = other.allowGroupSwitching;
primaryRefreshRateRange.min = other.primaryRefreshRateRange.min;
primaryRefreshRateRange.max = other.primaryRefreshRateRange.max;
appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 86691848c363..9437677d8ab8 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -817,6 +817,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
getDisplayTokenLocked(),
new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId,
+ mDisplayModeSpecs.allowGroupSwitching,
mDisplayModeSpecs.primaryRefreshRateRange.min,
mDisplayModeSpecs.primaryRefreshRateRange.max,
mDisplayModeSpecs.appRequestRefreshRateRange.min,
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 6ff661b1637e..5fddae53d632 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -66,10 +66,10 @@ final class Constants {
public static final int ADDR_PLAYBACK_3 = 11;
/** Logical address reserved for future usage */
- public static final int ADDR_RESERVED_1 = 12;
+ public static final int ADDR_BACKUP_1 = 12;
/** Logical address reserved for future usage */
- public static final int ADDR_RESERVED_2 = 13;
+ public static final int ADDR_BACKUP_2 = 13;
/** Logical address for TV other than the one assigned with {@link #ADDR_TV} */
public static final int ADDR_SPECIFIC_USE = 14;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 652bf5a7a67f..e906a7c8132c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -37,6 +37,7 @@ import com.android.server.hdmi.cec.config.XmlParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -81,7 +82,7 @@ public class HdmiCecConfig {
@NonNull private final Context mContext;
@NonNull private final StorageAdapter mStorageAdapter;
- @Nullable private final CecSettings mProductConfig;
+ @Nullable private final CecSettings mSystemConfig;
@Nullable private final CecSettings mVendorOverride;
/**
@@ -129,14 +130,14 @@ public class HdmiCecConfig {
@VisibleForTesting
HdmiCecConfig(@NonNull Context context,
@NonNull StorageAdapter storageAdapter,
- @Nullable CecSettings productConfig,
+ @Nullable CecSettings systemConfig,
@Nullable CecSettings vendorOverride) {
mContext = context;
mStorageAdapter = storageAdapter;
- mProductConfig = productConfig;
+ mSystemConfig = systemConfig;
mVendorOverride = vendorOverride;
- if (mProductConfig == null) {
- Slog.i(TAG, "CEC master configuration XML missing.");
+ if (mSystemConfig == null) {
+ Slog.i(TAG, "CEC system configuration XML missing.");
}
if (mVendorOverride == null) {
Slog.i(TAG, "CEC OEM configuration override XML missing.");
@@ -145,7 +146,7 @@ public class HdmiCecConfig {
HdmiCecConfig(@NonNull Context context) {
this(context, new StorageAdapter(),
- readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(),
+ readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(),
ETC_DIR, CONFIG_FILE)),
readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
ETC_DIR, CONFIG_FILE)));
@@ -168,9 +169,32 @@ public class HdmiCecConfig {
return null;
}
+ @NonNull
+ @VisibleForTesting
+ static HdmiCecConfig createFromStrings(@NonNull Context context,
+ @NonNull StorageAdapter storageAdapter,
+ @Nullable String systemConfigXml,
+ @Nullable String vendorOverrideXml) {
+ CecSettings systemConfig = null;
+ CecSettings vendorOverride = null;
+ try {
+ if (systemConfigXml != null) {
+ systemConfig = XmlParser.read(
+ new ByteArrayInputStream(systemConfigXml.getBytes()));
+ }
+ if (vendorOverrideXml != null) {
+ vendorOverride = XmlParser.read(
+ new ByteArrayInputStream(vendorOverrideXml.getBytes()));
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
+ }
+ return new HdmiCecConfig(context, storageAdapter, systemConfig, vendorOverride);
+ }
+
@Nullable
private Setting getSetting(@NonNull String name) {
- if (mProductConfig == null) {
+ if (mSystemConfig == null) {
return null;
}
if (mVendorOverride != null) {
@@ -181,8 +205,8 @@ public class HdmiCecConfig {
}
}
}
- // If not found, try the product config.
- for (Setting setting : mProductConfig.getSetting()) {
+ // If not found, try the system config.
+ for (Setting setting : mSystemConfig.getSetting()) {
if (setting.getName().equals(name)) {
return setting;
}
@@ -254,11 +278,11 @@ public class HdmiCecConfig {
* Returns a list of all settings based on the XML metadata.
*/
public @CecSettingName List<String> getAllSettings() {
- if (mProductConfig == null) {
+ if (mSystemConfig == null) {
return new ArrayList<String>();
}
List<String> allSettings = new ArrayList<String>();
- for (Setting setting : mProductConfig.getSetting()) {
+ for (Setting setting : mSystemConfig.getSetting()) {
allSettings.add(setting.getName());
}
return allSettings;
@@ -268,12 +292,12 @@ public class HdmiCecConfig {
* Returns a list of user-modifiable settings based on the XML metadata.
*/
public @CecSettingName List<String> getUserSettings() {
- if (mProductConfig == null) {
+ if (mSystemConfig == null) {
return new ArrayList<String>();
}
Set<String> userSettings = new HashSet<String>();
- // First read from the product config.
- for (Setting setting : mProductConfig.getSetting()) {
+ // First read from the system config.
+ for (Setting setting : mSystemConfig.getSetting()) {
if (setting.getUserConfigurable()) {
userSettings.add(setting.getName());
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index efe730231d36..19dc017a35d5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -85,6 +85,8 @@ final class HdmiCecController {
private static final int NUM_LOGICAL_ADDRESS = 16;
+ private static final int MAX_DEDICATED_ADDRESS = 11;
+
private static final int MAX_HDMI_MESSAGE_HISTORY = 250;
private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
@@ -104,7 +106,7 @@ final class HdmiCecController {
private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
@Override
public boolean test(Integer address) {
- return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM;
+ return HdmiUtils.isEligibleAddressForDevice(Constants.ADDR_AUDIO_SYSTEM, address);
}
};
@@ -195,42 +197,46 @@ final class HdmiCecController {
});
}
+ /**
+ * Address allocation will check the following addresses (in order):
+ * <ul>
+ * <li>Given preferred logical address (if the address is valid for the given device
+ * type)</li>
+ * <li>All dedicated logical addresses for the given device type</li>
+ * <li>Backup addresses, if valid for the given device type</li>
+ * </ul>
+ */
@IoThreadOnly
private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
final AllocateAddressCallback callback) {
assertRunOnIoThread();
- int startAddress = preferredAddress;
- // If preferred address is "unregistered", start address will be the smallest
- // address matched with the given device type.
- if (preferredAddress == Constants.ADDR_UNREGISTERED) {
- for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
- if (deviceType == HdmiUtils.getTypeFromAddress(i)) {
- startAddress = i;
- break;
- }
+ List<Integer> logicalAddressesToPoll = new ArrayList<>();
+ if (HdmiUtils.isEligibleAddressForDevice(deviceType, preferredAddress)) {
+ logicalAddressesToPoll.add(preferredAddress);
+ }
+ for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
+ if (!logicalAddressesToPoll.contains(i) && HdmiUtils.isEligibleAddressForDevice(
+ deviceType, i) && HdmiUtils.isEligibleAddressForCecVersion(
+ mService.getCecVersion(), i)) {
+ logicalAddressesToPoll.add(i);
}
}
int logicalAddress = Constants.ADDR_UNREGISTERED;
- // Iterates all possible addresses which has the same device type.
- for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
- int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
- if (curAddress != Constants.ADDR_UNREGISTERED
- && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
- boolean acked = false;
- for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
- if (sendPollMessage(curAddress, curAddress, 1)) {
- acked = true;
- break;
- }
- }
- // If sending <Polling Message> failed, it becomes new logical address for the
- // device because no device uses it as logical address of the device.
- if (!acked) {
- logicalAddress = curAddress;
+ for (Integer logicalAddressToPoll : logicalAddressesToPoll) {
+ boolean acked = false;
+ for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
+ if (sendPollMessage(logicalAddressToPoll, logicalAddressToPoll, 1)) {
+ acked = true;
break;
}
}
+ // If sending <Polling Message> failed, it becomes new logical address for the
+ // device because no device uses it as logical address of the device.
+ if (!acked) {
+ logicalAddress = logicalAddressToPoll;
+ break;
+ }
}
final int assignedAddress = logicalAddress;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 4325f797416e..960510693f6a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -246,6 +246,10 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
// Indicates if current device is the active source or not
@ServiceThreadOnly
protected boolean isActiveSource() {
+ if (getDeviceInfo() == null) {
+ return false;
+ }
+
return getActiveSource().equals(getDeviceInfo().getLogicalAddress(),
getDeviceInfo().getPhysicalAddress());
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 93cdca2d0c83..c2e80caadd3b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1368,9 +1368,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private boolean checkRecorder(int recorderAddress) {
HdmiDeviceInfo device = mService.getHdmiCecNetwork().getCecDeviceInfo(recorderAddress);
- return (device != null)
- && (HdmiUtils.getTypeFromAddress(recorderAddress)
- == HdmiDeviceInfo.DEVICE_RECORDER);
+ return (device != null) && (HdmiUtils.isEligibleAddressForDevice(
+ HdmiDeviceInfo.DEVICE_RECORDER, recorderAddress));
}
private boolean checkRecordSource(byte[] recordSource) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index d4593afe18fe..7d766285bdfa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -152,8 +152,10 @@ public class HdmiCecMessageValidator {
addValidationInfo(Constants.MESSAGE_SET_OSD_NAME, new AsciiValidator(1, 14), DEST_DIRECT);
// Messages for the Device Menu Control.
- addValidationInfo(Constants.MESSAGE_MENU_REQUEST, oneByteValidator, DEST_DIRECT);
- addValidationInfo(Constants.MESSAGE_MENU_STATUS, oneByteValidator, DEST_DIRECT);
+ addValidationInfo(
+ Constants.MESSAGE_MENU_REQUEST, new OneByteRangeValidator(0x00, 0x02), DEST_DIRECT);
+ addValidationInfo(
+ Constants.MESSAGE_MENU_STATUS, new OneByteRangeValidator(0x00, 0x01), DEST_DIRECT);
// Messages for the Remote Control Passthrough.
// TODO: Parse the first parameter and determine if it can have the next parameter.
@@ -161,7 +163,10 @@ public class HdmiCecMessageValidator {
new VariableLengthValidator(1, 2), DEST_DIRECT);
// Messages for the Power Status.
- addValidationInfo(Constants.MESSAGE_REPORT_POWER_STATUS, oneByteValidator, DEST_DIRECT);
+ addValidationInfo(
+ Constants.MESSAGE_REPORT_POWER_STATUS,
+ new OneByteRangeValidator(0x00, 0x03),
+ DEST_DIRECT);
// Messages for the General Protocol.
addValidationInfo(Constants.MESSAGE_FEATURE_ABORT,
@@ -173,12 +178,20 @@ public class HdmiCecMessageValidator {
new FixedLengthValidator(3), DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
oneByteValidator, DEST_DIRECT);
- addValidationInfo(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, oneByteValidator, DEST_ALL);
- addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
- oneByteValidator, DEST_DIRECT);
+ addValidationInfo(
+ Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
+ new OneByteRangeValidator(0x00, 0x01),
+ DEST_ALL);
+ addValidationInfo(
+ Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
+ new OneByteRangeValidator(0x00, 0x01),
+ DEST_DIRECT);
// Messages for the Audio Rate Control.
- addValidationInfo(Constants.MESSAGE_SET_AUDIO_RATE, oneByteValidator, DEST_DIRECT);
+ addValidationInfo(
+ Constants.MESSAGE_SET_AUDIO_RATE,
+ new OneByteRangeValidator(0x00, 0x06),
+ DEST_DIRECT);
// All Messages for the ARC have no parameters.
@@ -441,4 +454,22 @@ public class HdmiCecMessageValidator {
&& isValidAsciiString(params, 1, 14));
}
}
+
+ /** Check if the given parameters are one byte parameters and within range. */
+ private class OneByteRangeValidator implements ParameterValidator {
+ private final int mMinValue, mMaxValue;
+
+ OneByteRangeValidator(int minValue, int maxValue) {
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ }
+
+ @Override
+ public int isValid(byte[] params) {
+ if (params.length < 1) {
+ return ERROR_PARAMETER_SHORT;
+ }
+ return toErrorCode(isWithinRange(params[0], mMinValue, mMaxValue));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b4a765e10fca..da4c6f115d55 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1561,7 +1561,7 @@ public class HdmiControlService extends SystemService {
new HdmiDeviceInfo(activeSource.logicalAddress,
activeSource.physicalAddress,
pathToPortId(activeSource.physicalAddress),
- HdmiUtils.getTypeFromAddress(activeSource.logicalAddress), 0,
+ HdmiUtils.getTypeFromAddress(activeSource.logicalAddress).get(0), 0,
HdmiUtils.getDefaultDeviceName(activeSource.logicalAddress))
:
new HdmiDeviceInfo(activeSource.physicalAddress,
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index a6951ef1958f..45aaa73b757b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -16,6 +16,10 @@
package com.android.server.hdmi;
+import static com.android.server.hdmi.Constants.ADDR_BACKUP_1;
+import static com.android.server.hdmi.Constants.ADDR_BACKUP_2;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.util.Slog;
@@ -29,6 +33,8 @@ import com.android.server.hdmi.Constants.AudioCodec;
import com.android.server.hdmi.Constants.FeatureOpcode;
import com.android.server.hdmi.Constants.PathRelationship;
+import com.google.android.collect.Lists;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -37,6 +43,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -48,23 +55,38 @@ final class HdmiUtils {
private static final String TAG = "HdmiUtils";
- private static final int[] ADDRESS_TO_TYPE = {
- HdmiDeviceInfo.DEVICE_TV, // ADDR_TV
- HdmiDeviceInfo.DEVICE_RECORDER, // ADDR_RECORDER_1
- HdmiDeviceInfo.DEVICE_RECORDER, // ADDR_RECORDER_2
- HdmiDeviceInfo.DEVICE_TUNER, // ADDR_TUNER_1
- HdmiDeviceInfo.DEVICE_PLAYBACK, // ADDR_PLAYBACK_1
- HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, // ADDR_AUDIO_SYSTEM
- HdmiDeviceInfo.DEVICE_TUNER, // ADDR_TUNER_2
- HdmiDeviceInfo.DEVICE_TUNER, // ADDR_TUNER_3
- HdmiDeviceInfo.DEVICE_PLAYBACK, // ADDR_PLAYBACK_2
- HdmiDeviceInfo.DEVICE_RECORDER, // ADDR_RECORDER_3
- HdmiDeviceInfo.DEVICE_TUNER, // ADDR_TUNER_4
- HdmiDeviceInfo.DEVICE_PLAYBACK, // ADDR_PLAYBACK_3
- HdmiDeviceInfo.DEVICE_RESERVED,
- HdmiDeviceInfo.DEVICE_RESERVED,
- HdmiDeviceInfo.DEVICE_TV, // ADDR_SPECIFIC_USE
- };
+ private static final Map<Integer, List<Integer>> ADDRESS_TO_TYPE =
+ new HashMap<Integer, List<Integer>>() {
+ {
+ put(Constants.ADDR_TV, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV));
+ put(Constants.ADDR_RECORDER_1,
+ Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER));
+ put(Constants.ADDR_RECORDER_2,
+ Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER));
+ put(Constants.ADDR_TUNER_1, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER));
+ put(Constants.ADDR_PLAYBACK_1,
+ Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK));
+ put(Constants.ADDR_AUDIO_SYSTEM,
+ Lists.newArrayList(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM));
+ put(Constants.ADDR_TUNER_2, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER));
+ put(Constants.ADDR_TUNER_3, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER));
+ put(Constants.ADDR_PLAYBACK_2,
+ Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK));
+ put(Constants.ADDR_RECORDER_3,
+ Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER));
+ put(Constants.ADDR_TUNER_4, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER));
+ put(Constants.ADDR_PLAYBACK_3,
+ Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK));
+ put(Constants.ADDR_BACKUP_1, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ HdmiDeviceInfo.DEVICE_RECORDER, HdmiDeviceInfo.DEVICE_TUNER,
+ HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR));
+ put(Constants.ADDR_BACKUP_2, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ HdmiDeviceInfo.DEVICE_RECORDER, HdmiDeviceInfo.DEVICE_TUNER,
+ HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR));
+ put(Constants.ADDR_SPECIFIC_USE, Lists.newArrayList(ADDR_TV));
+ put(Constants.ADDR_UNREGISTERED, Collections.emptyList());
+ }
+ };
private static final String[] DEFAULT_NAMES = {
"TV",
@@ -79,8 +101,8 @@ final class HdmiUtils {
"Recorder_3",
"Tuner_4",
"Playback_3",
- "Reserved_1",
- "Reserved_2",
+ "Backup_1",
+ "Backup_2",
"Secondary_TV",
};
@@ -101,21 +123,36 @@ final class HdmiUtils {
* @return true if the given address is valid
*/
static boolean isValidAddress(int address) {
- return (Constants.ADDR_TV <= address && address <= Constants.ADDR_SPECIFIC_USE);
+ return (ADDR_TV <= address && address <= Constants.ADDR_SPECIFIC_USE);
+ }
+
+ static boolean isEligibleAddressForDevice(int deviceType, int logicalAddress) {
+ return isValidAddress(logicalAddress)
+ && ADDRESS_TO_TYPE.get(logicalAddress).contains(deviceType);
+ }
+
+ static boolean isEligibleAddressForCecVersion(int cecVersion, int logicalAddress) {
+ if (isValidAddress(logicalAddress)) {
+ if (logicalAddress == ADDR_BACKUP_1 || logicalAddress == ADDR_BACKUP_2) {
+ return cecVersion == Constants.VERSION_2_0;
+ }
+ return true;
+ }
+ return false;
}
/**
* Return the device type for the given logical address.
*
- * @param address logical address
+ * @param logicalAddress logical address
* @return device type for the given logical address; DEVICE_INACTIVE
* if the address is not valid.
*/
- static int getTypeFromAddress(int address) {
- if (isValidAddress(address)) {
- return ADDRESS_TO_TYPE[address];
+ static List<Integer> getTypeFromAddress(int logicalAddress) {
+ if (isValidAddress(logicalAddress)) {
+ return ADDRESS_TO_TYPE.get(logicalAddress);
}
- return HdmiDeviceInfo.DEVICE_INACTIVE;
+ return Lists.newArrayList(HdmiDeviceInfo.DEVICE_INACTIVE);
}
/**
@@ -142,10 +179,10 @@ final class HdmiUtils {
* @throws IllegalArgumentException
*/
static void verifyAddressType(int logicalAddress, int deviceType) {
- int actualDeviceType = getTypeFromAddress(logicalAddress);
- if (actualDeviceType != deviceType) {
+ List<Integer> actualDeviceTypes = getTypeFromAddress(logicalAddress);
+ if (!actualDeviceTypes.contains(deviceType)) {
throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
- + ", Actual:" + actualDeviceType);
+ + ", Actual:" + actualDeviceTypes);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index ece78bfa2769..a7f34ed85e0d 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -259,7 +259,7 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
}
private void mayDisableSystemAudioAndARC(int address) {
- if (HdmiUtils.getTypeFromAddress(address) != HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
+ if (HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) {
return;
}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index edc7bd95a017..a307ea33abf7 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -183,8 +183,8 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
// Consume CEC messages we already got for this newly found device.
tv().processDelayedMessages(mDeviceLogicalAddress);
- if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress)
- == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
+ if (HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ mDeviceLogicalAddress)) {
tv().onNewAvrAdded(deviceInfo);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 047f1742f2ca..16a4b7250598 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1713,7 +1713,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mSettings, context);
mMenuController = new InputMethodMenuController(this);
- mTracingThread = new HandlerThread("android.tracing", Process.THREAD_PRIORITY_BACKGROUND);
+ mTracingThread = new HandlerThread("android.tracing", Process.THREAD_PRIORITY_FOREGROUND);
mTracingThread.start();
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 9ca4d35f4579..9c48d23dade6 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -306,9 +306,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private void removeLocationProviderManager(LocationProviderManager manager) {
synchronized (mProviderManagers) {
- Preconditions.checkState(getLocationProviderManager(manager.getName()) == manager);
-
- mProviderManagers.remove(manager);
+ boolean removed = mProviderManagers.remove(manager);
+ Preconditions.checkArgument(removed);
manager.setMockProvider(null);
manager.setRealProvider(null);
manager.stopManager();
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index b4a172393ba6..f25c65192066 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -491,7 +491,7 @@ class LocationProviderManager extends
builder.setIntervalMillis(MIN_COARSE_INTERVAL_MS);
}
if (baseRequest.getMinUpdateIntervalMillis() < MIN_COARSE_INTERVAL_MS) {
- builder.clearMinUpdateIntervalMillis();
+ builder.setMinUpdateIntervalMillis(MIN_COARSE_INTERVAL_MS);
}
}
@@ -1197,19 +1197,19 @@ class LocationProviderManager extends
public void stopManager() {
synchronized (mLock) {
- mUserHelper.removeListener(mUserChangedListener);
- mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
-
- mStarted = false;
-
final long identity = Binder.clearCallingIdentity();
try {
onEnabledChanged(UserHandle.USER_ALL);
removeRegistrationIf(key -> true);
- mEnabledListeners.clear();
} finally {
Binder.restoreCallingIdentity(identity);
}
+
+ mUserHelper.removeListener(mUserChangedListener);
+ mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
+
+ Preconditions.checkState(mEnabledListeners.isEmpty());
+ mStarted = false;
}
}
@@ -1408,6 +1408,7 @@ class LocationProviderManager extends
Location location;
synchronized (mLock) {
+ Preconditions.checkState(mStarted);
LastLocation lastLocation = mLastLocations.get(userId);
if (lastLocation == null) {
location = null;
@@ -1429,6 +1430,7 @@ class LocationProviderManager extends
public void injectLastLocation(Location location, int userId) {
synchronized (mLock) {
+ Preconditions.checkState(mStarted);
if (getLastLocationUnsafe(userId, PERMISSION_FINE, false, Long.MAX_VALUE) == null) {
setLastLocation(location, userId);
}
@@ -1476,6 +1478,7 @@ class LocationProviderManager extends
permissionLevel);
synchronized (mLock) {
+ Preconditions.checkState(mStarted);
final long ident = Binder.clearCallingIdentity();
try {
putRegistration(callback.asBinder(), registration);
@@ -1517,6 +1520,7 @@ class LocationProviderManager extends
permissionLevel);
synchronized (mLock) {
+ Preconditions.checkState(mStarted);
final long ident = Binder.clearCallingIdentity();
try {
putRegistration(listener.asBinder(), registration);
@@ -1535,6 +1539,7 @@ class LocationProviderManager extends
permissionLevel);
synchronized (mLock) {
+ Preconditions.checkState(mStarted);
final long identity = Binder.clearCallingIdentity();
try {
putRegistration(pendingIntent, registration);
@@ -1546,6 +1551,7 @@ class LocationProviderManager extends
public void unregisterLocationRequest(ILocationListener listener) {
synchronized (mLock) {
+ Preconditions.checkState(mStarted);
final long identity = Binder.clearCallingIdentity();
try {
removeRegistration(listener.asBinder());
@@ -1557,6 +1563,7 @@ class LocationProviderManager extends
public void unregisterLocationRequest(PendingIntent pendingIntent) {
synchronized (mLock) {
+ Preconditions.checkState(mStarted);
final long identity = Binder.clearCallingIdentity();
try {
removeRegistration(pendingIntent);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 71e7c8adc5db..12c24d418611 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -552,7 +552,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}.
final PendingIntent pollIntent =
- PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
+ PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL),
+ PendingIntent.FLAG_IMMUTABLE);
final long currentRealtime = SystemClock.elapsedRealtime();
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 4040f4189698..ea7779a4dc8f 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -110,6 +110,7 @@ abstract public class ManagedServices {
static final String ATT_IS_PRIMARY = "primary";
static final String ATT_VERSION = "version";
static final String ATT_DEFAULTS = "defaults";
+ static final String ATT_USER_SET = "user_set_services";
static final int DB_VERSION = 3;
@@ -150,7 +151,12 @@ abstract public class ManagedServices {
// List of approved packages or components (by user, then by primary/secondary) that are
// allowed to be bound as managed services. A package or component appearing in this list does
// not mean that we are currently bound to said package/component.
- private ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
+ protected ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
+
+ // List of packages or components (by user) that are configured to be enabled/disabled
+ // explicitly by the user
+ @GuardedBy("mApproved")
+ protected ArrayMap<Integer, ArraySet<String>> mUserSetServices = new ArrayMap<>();
// True if approved services are stored in xml, not settings.
private boolean mUseXml;
@@ -333,6 +339,12 @@ abstract public class ManagedServices {
}
}
}
+
+ pw.println(" Has user set:");
+ Set<Integer> userIds = mUserSetServices.keySet();
+ for (int userId : userIds) {
+ pw.println(" userId=" + userId + " value=" + mUserSetServices.get(userId));
+ }
}
pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
@@ -465,12 +477,20 @@ abstract public class ManagedServices {
for (int j = 0; j < M; j++) {
final boolean isPrimary = approvedByType.keyAt(j);
final Set<String> approved = approvedByType.valueAt(j);
- if (approved != null) {
- String allowedItems = String.join(ENABLED_SERVICES_SEPARATOR, approved);
+ final Set<String> userSet = mUserSetServices.get(approvedUserId);
+ if (approved != null || userSet != null) {
+ String allowedItems = approved == null
+ ? ""
+ : String.join(ENABLED_SERVICES_SEPARATOR, approved);
out.startTag(null, TAG_MANAGED_SERVICES);
out.attribute(null, ATT_APPROVED_LIST, allowedItems);
out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId));
out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary));
+ if (userSet != null) {
+ String userSetItems =
+ String.join(ENABLED_SERVICES_SEPARATOR, userSet);
+ out.attribute(null, ATT_USER_SET, userSetItems);
+ }
writeExtraAttributes(out, approvedUserId);
out.endTag(null, TAG_MANAGED_SERVICES);
@@ -559,11 +579,12 @@ abstract public class ManagedServices {
? userId : XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
final boolean isPrimary =
XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true);
+ final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
readExtraAttributes(tag, parser, resolvedUserId);
if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(
getPackageName(approved), resolvedUserId, getRequiredPermission())) {
if (mUm.getUserInfo(resolvedUserId) != null) {
- addApprovedList(approved, resolvedUserId, isPrimary);
+ addApprovedList(approved, resolvedUserId, isPrimary, userSet);
}
mUseXml = true;
}
@@ -632,9 +653,16 @@ abstract public class ManagedServices {
}
protected void addApprovedList(String approved, int userId, boolean isPrimary) {
+ addApprovedList(approved, userId, isPrimary, approved);
+ }
+
+ protected void addApprovedList(String approved, int userId, boolean isPrimary, String userSet) {
if (TextUtils.isEmpty(approved)) {
approved = "";
}
+ if (userSet == null) {
+ userSet = approved;
+ }
synchronized (mApproved) {
ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
if (approvedByType == null) {
@@ -655,6 +683,19 @@ abstract public class ManagedServices {
approvedList.add(approvedItem);
}
}
+
+ ArraySet<String> userSetList = mUserSetServices.get(userId);
+ if (userSetList == null) {
+ userSetList = new ArraySet<>();
+ mUserSetServices.put(userId, userSetList);
+ }
+ String[] userSetArray = userSet.split(ENABLED_SERVICES_SEPARATOR);
+ for (String pkgOrComponent : userSetArray) {
+ String approvedItem = getApprovedValue(pkgOrComponent);
+ if (approvedItem != null) {
+ userSetList.add(approvedItem);
+ }
+ }
}
}
@@ -664,8 +705,14 @@ abstract public class ManagedServices {
protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
boolean isPrimary, boolean enabled) {
+ setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, true);
+ }
+
+ protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
+ boolean isPrimary, boolean enabled, boolean userSet) {
Slog.i(TAG,
- (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + pkgOrComponent);
+ (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " "
+ + pkgOrComponent + " (userSet: " + userSet + ")");
synchronized (mApproved) {
ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId);
if (allowedByType == null) {
@@ -686,6 +733,16 @@ abstract public class ManagedServices {
approved.remove(approvedItem);
}
}
+ ArraySet<String> userSetServices = mUserSetServices.get(userId);
+ if (userSetServices == null) {
+ userSetServices = new ArraySet<>();
+ mUserSetServices.put(userId, userSetServices);
+ }
+ if (userSet) {
+ userSetServices.add(pkgOrComponent);
+ } else {
+ userSetServices.remove(pkgOrComponent);
+ }
}
rebindServices(false, userId);
@@ -761,6 +818,13 @@ abstract public class ManagedServices {
return false;
}
+ boolean isPackageOrComponentUserSet(String pkgOrComponent, int userId) {
+ synchronized (mApproved) {
+ ArraySet<String> services = mUserSetServices.get(userId);
+ return services != null && services.contains(pkgOrComponent);
+ }
+ }
+
protected boolean isPackageAllowed(String pkg, int userId) {
if (pkg == null) {
return false;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8ea432634e61..4f4f3c64b1b4 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -97,6 +97,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static com.android.internal.util.CollectionUtils.emptyIfNull;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -304,6 +305,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -714,7 +716,7 @@ public class NotificationManagerService extends SystemService {
try {
getBinderService().setNotificationListenerAccessGrantedForUser(cn,
- userId, true);
+ userId, true, true);
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -4822,9 +4824,9 @@ public class NotificationManagerService extends SystemService {
@Override
public void setNotificationListenerAccessGranted(ComponentName listener,
- boolean granted) throws RemoteException {
+ boolean granted, boolean userSet) throws RemoteException {
setNotificationListenerAccessGrantedForUser(
- listener, getCallingUserHandle().getIdentifier(), granted);
+ listener, getCallingUserHandle().getIdentifier(), granted, userSet);
}
@Override
@@ -4836,17 +4838,21 @@ public class NotificationManagerService extends SystemService {
@Override
public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
- boolean granted) {
+ boolean granted, boolean userSet) {
Objects.requireNonNull(listener);
checkNotificationListenerAccess();
+ if (!userSet && isNotificationListenerAccessUserSet(listener)) {
+ // Don't override user's choice
+ return;
+ }
final long identity = Binder.clearCallingIdentity();
try {
if (mAllowedManagedServicePackages.test(
listener.getPackageName(), userId, mListeners.getRequiredPermission())) {
mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
- userId, false, granted);
+ userId, false, granted, userSet);
mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
- userId, true, granted);
+ userId, true, granted, userSet);
getContext().sendBroadcastAsUser(new Intent(
ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
@@ -4861,6 +4867,11 @@ public class NotificationManagerService extends SystemService {
}
}
+ private boolean isNotificationListenerAccessUserSet(ComponentName listener) {
+ return mListeners.isPackageOrComponentUserSet(listener.flattenToString(),
+ getCallingUserHandle().getIdentifier());
+ }
+
@Override
public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
int userId, boolean granted) {
@@ -8830,14 +8841,12 @@ public class NotificationManagerService extends SystemService {
public class NotificationAssistants extends ManagedServices {
static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
- private static final String ATT_USER_SET = "user_set";
private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "q_allowed_adjustments";
private static final String ATT_TYPES = "types";
private final Object mLock = new Object();
@GuardedBy("mLock")
- private ArrayMap<Integer, Boolean> mUserSetMap = new ArrayMap<>();
private Set<String> mAllowedAdjustments = new ArraySet<>();
@Override
@@ -9026,22 +9035,33 @@ public class NotificationManagerService extends SystemService {
boolean hasUserSet(int userId) {
synchronized (mLock) {
- return mUserSetMap.getOrDefault(userId, false);
+ ArraySet<String> userSetServices = mUserSetServices.get(userId);
+ if (userSetServices == null) {
+ // Legacy case - no data means user-set, unless no assistant is set
+ return !mApproved.isEmpty();
+ }
+ Map<Boolean, ArraySet<String>> approvedByType = emptyIfNull(mApproved.get(userId));
+ return userSetServices.containsAll(emptyIfNull(approvedByType.get(true)))
+ && userSetServices.containsAll(emptyIfNull(approvedByType.get(false)));
}
}
void setUserSet(int userId, boolean set) {
synchronized (mLock) {
- mUserSetMap.put(userId, set);
+ ArraySet<String> userSetServices = new ArraySet<>();
+ if (set) {
+ ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
+ if (approvedByType != null) {
+ for (int i = 0; i < approvedByType.size(); i++) {
+ userSetServices.addAll(approvedByType.valueAt(i));
+ }
+ }
+ }
+ mUserSetServices.put(userId, userSetServices);
}
}
@Override
- protected void writeExtraAttributes(XmlSerializer out, int userId) throws IOException {
- out.attribute(null, ATT_USER_SET, Boolean.toString(hasUserSet(userId)));
- }
-
- @Override
protected void readExtraAttributes(String tag, XmlPullParser parser, int userId)
throws IOException {
boolean userSet = XmlUtils.readBooleanAttribute(parser, ATT_USER_SET, false);
@@ -9279,18 +9299,6 @@ public class NotificationManagerService extends SystemService {
super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled);
}
- @Override
- public void dump(PrintWriter pw, DumpFilter filter) {
- super.dump(pw, filter);
- pw.println(" Has user set:");
- synchronized (mLock) {
- Set<Integer> userIds = mUserSetMap.keySet();
- for (int userId : userIds) {
- pw.println(" userId=" + userId + " value=" + mUserSetMap.get(userId));
- }
- }
- }
-
private boolean isVerboseLogEnabled() {
return Log.isLoggable("notification_assistant", Log.VERBOSE);
}
@@ -9307,8 +9315,8 @@ public class NotificationManagerService extends SystemService {
@Override
protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
- boolean isPrimary, boolean enabled) {
- super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled);
+ boolean isPrimary, boolean enabled, boolean userSet) {
+ super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, userSet);
getContext().sendBroadcastAsUser(
new Intent(ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED)
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index 73272a012671..78c60d559ea9 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -214,7 +214,8 @@ public class NotificationShellCmd extends ShellCommand {
if (peekNextArg() != null) {
userId = Integer.parseInt(getNextArgRequired());
}
- mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, true);
+ mBinderService.setNotificationListenerAccessGrantedForUser(
+ cn, userId, true, true);
}
break;
case "disallow_listener": {
@@ -227,7 +228,8 @@ public class NotificationShellCmd extends ShellCommand {
if (peekNextArg() != null) {
userId = Integer.parseInt(getNextArgRequired());
}
- mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, false);
+ mBinderService.setNotificationListenerAccessGrantedForUser(
+ cn, userId, false, true);
}
break;
case "allow_assistant": {
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 8f05636eed9c..f5d648910d90 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -39,7 +39,7 @@ public class ZenLog {
// the ZenLog is *very* verbose, so be careful about setting this to true
private static final boolean DEBUG = false;
- private static final int SIZE = Build.IS_DEBUGGABLE ? 100 : 20;
+ private static final int SIZE = Build.IS_DEBUGGABLE ? 200 : 100;
private static final long[] TIMES = new long[SIZE];
private static final int[] TYPES = new int[SIZE];
@@ -136,9 +136,14 @@ public class ZenLog {
public static void traceConfig(String reason, ZenModeConfig oldConfig,
ZenModeConfig newConfig) {
- append(TYPE_CONFIG, reason
- + "," + (newConfig != null ? newConfig.toString() : null)
- + "," + ZenModeConfig.diff(oldConfig, newConfig));
+ ZenModeConfig.Diff diff = ZenModeConfig.diff(oldConfig, newConfig);
+ if (diff.isEmpty()) {
+ append(TYPE_CONFIG, reason + " no changes");
+ } else {
+ append(TYPE_CONFIG, reason
+ + ",\n" + (newConfig != null ? newConfig.toString() : null)
+ + ",\n" + ZenModeConfig.diff(oldConfig, newConfig));
+ }
}
public static void traceDisableEffects(NotificationRecord record, String reason) {
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 571f79915484..50b4d438984c 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -108,7 +108,7 @@ public class ZenModeConditions implements ConditionProviders.Callback {
@Override
public void onServiceAdded(ComponentName component) {
if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
- mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded");
+ mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded:" + component);
}
@Override
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 7992fea4a675..a12932a88487 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -1018,29 +1018,17 @@ public abstract class ApexManager {
// the /apex directory is just a symlink to /system/apex.
List<ActiveApexInfo> result = new ArrayList<>();
File apexDir = Environment.getApexDirectory();
- // In flattened configuration, init special-case the art directory and bind-mounts
- // com.android.art.{release|debug} to com.android.art. At the time of writing, these
- // directories are copied from the kArtApexDirNames variable in
- // system/core/init/mount_namespace.cpp.
- String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
if (apexDir.isDirectory()) {
File[] files = apexDir.listFiles();
// listFiles might be null if system server doesn't have permission to read
// a directory.
if (files != null) {
for (File file : files) {
- if (file.isDirectory() && !file.getName().contains("@")) {
- boolean skip = false;
- for (String skipDir : skipDirs) {
- if (file.getName().equals(skipDir)) {
- skip = true;
- break;
- }
- }
- if (!skip) {
- result.add(
- new ActiveApexInfo(file, Environment.getRootDirectory()));
- }
+ if (file.isDirectory() && !file.getName().contains("@")
+ // In flattened configuration, init special-cases the art directory
+ // and bind-mounts com.android.art.debug to com.android.art.
+ && !file.getName().equals("com.android.art.debug")) {
+ result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
}
}
}
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index c0329b2b2fe6..efbdfeaaeb93 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -918,7 +918,7 @@ class InstantAppRegistry {
try {
for (String grantedPermission : appInfo.getGrantedPermissions()) {
final boolean propagatePermission =
- mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission);
+ mPermissionManager.canPropagatePermissionToInstantApp(grantedPermission);
if (propagatePermission && pkg.getRequestedPermissions().contains(
grantedPermission)) {
mService.grantRuntimePermission(pkg.getPackageName(), grantedPermission,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7eb4fd968c36..4fdb9fbb3b3f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -375,7 +375,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal;
@@ -2742,7 +2742,6 @@ public class PackageManagerService extends IPackageManager.Stub
new UserDataPreparer(installer, installLock, context, onlyCore),
lock),
(i, pm) -> new Settings(Environment.getDataDirectory(),
- i.getPermissionManagerServiceInternal().getPermissionSettings(),
RuntimePermissionsPersistence.createInstance(),
i.getPermissionManagerServiceInternal(), lock),
(i, pm) -> AppsFilter.create(pm.mPmInternal, i),
@@ -3173,6 +3172,8 @@ public class PackageManagerService extends IPackageManager.Stub
/* excludePreCreated= */ false));
t.traceEnd();
+ mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
+
// Clean up orphaned packages for which the code path doesn't exist
// and they are an update to a system app - caused by bug/32321269
final int packageSettingCount = mSettings.mPackages.size();
@@ -3597,7 +3598,7 @@ public class PackageManagerService extends IPackageManager.Stub
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
- mPermissionManager.readStateFromPackageSettingsTEMP();
+ mPermissionManager.readLegacyPermissionStateTEMP();
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
@@ -11433,7 +11434,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (verifyPackageUpdateLPr(orig, pkg)) {
Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ pkg.getPackageName());
- mSettings.mPermissions.transferPermissions(origName, pkg.getPackageName());
+ mPermissionManager.transferPermissions(origName, pkg.getPackageName());
}
}
}
@@ -12305,9 +12306,8 @@ public class PackageManagerService extends IPackageManager.Stub
// user-installed version of the application will be ignored.
if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
if (mExpectingBetter.containsKey(pkg.getPackageName())) {
- logCriticalInfo(Log.WARN,
- "Relax SCAN_REQUIRE_KNOWN requirement for package "
- + pkg.getPackageName());
+ Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
+ + pkg.getPackageName());
} else {
PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName());
if (known != null) {
@@ -13326,13 +13326,21 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) {
final int callingUid = Binder.getCallingUid();
- PackageManagerServiceUtils
- .enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled", callingUid);
+ final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
+ || callingUid == Process.SYSTEM_UID;
+ if (!calledFromSystemOrPhone) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
+ "setSystemAppHiddenUntilInstalled");
+ }
+
synchronized (mLock) {
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null || !pkgSetting.isSystem()) {
return;
}
+ if (pkgSetting.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
+ throw new SecurityException("Only system or phone callers can modify core apps");
+ }
pkgSetting.getPkgState().setHiddenUntilInstalled(hidden);
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
if (disabledPs == null) {
@@ -13345,14 +13353,22 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) {
final int callingUid = Binder.getCallingUid();
- PackageManagerServiceUtils
- .enforceSystemOrPhoneCaller("setSystemAppInstallState", callingUid);
+ final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
+ || callingUid == Process.SYSTEM_UID;
+ if (!calledFromSystemOrPhone) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
+ "setSystemAppHiddenUntilInstalled");
+ }
+
synchronized (mLock) {
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
// The target app should always be in system
if (pkgSetting == null || !pkgSetting.isSystem()) {
return false;
}
+ if (pkgSetting.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
+ throw new SecurityException("Only system or phone callers can modify core apps");
+ }
// Check if the install state is the same
if (pkgSetting.getInstalled(userId) == installed) {
return false;
@@ -17928,7 +17944,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int N = ArrayUtils.size(parsedPackage.getPermissions());
for (int i = N - 1; i >= 0; i--) {
final ParsedPermission perm = parsedPackage.getPermissions().get(i);
- final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName());
+ final Permission bp = mPermissionManager.getPermissionTEMP(perm.getName());
// Don't allow anyone but the system to define ephemeral permissions.
if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
@@ -24216,9 +24232,9 @@ public class PackageManagerService extends IPackageManager.Stub
boolean readPermissionStateForUser(@UserIdInt int userId) {
synchronized (mPackages) {
- mPermissionManager.writeStateToPackageSettingsTEMP();
+ mPermissionManager.writeLegacyPermissionStateTEMP();
mSettings.readPermissionStateForUserSyncLPr(userId);
- mPermissionManager.readStateFromPackageSettingsTEMP();
+ mPermissionManager.readLegacyPermissionStateTEMP();
return mPmInternal.isPermissionUpgradeNeeded(userId);
}
}
@@ -26361,13 +26377,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
- * Temporary method that wraps mSettings.writeLPr() and calls
- * mPermissionManager.writeStateToPackageSettingsTEMP() beforehand.
+ * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's
+ * writeLegacyPermissionsTEMP() and writeLegacyPermissionStateTEMP() beforehand.
*
* TODO(zhanghai): This should be removed once we finish migration of permission storage.
*/
private void writeSettingsLPrTEMP() {
- mPermissionManager.writeStateToPackageSettingsTEMP();
+ mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
+ mPermissionManager.writeLegacyPermissionStateTEMP();
mSettings.writeLPr();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index e5dad8570254..b05fd47383fd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -103,7 +103,8 @@ import java.util.zip.GZIPInputStream;
* {@hide}
*/
public class PackageManagerServiceUtils {
- private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+ private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+ private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 3 * 1000 * 1000; // 3MB
public final static Predicate<PackageSetting> REMOVE_IF_NULL_PKG =
pkgSetting -> pkgSetting.pkg == null;
@@ -349,7 +350,12 @@ public class PackageManagerServiceUtils {
}
public static void dumpCriticalInfo(ProtoOutputStream proto) {
- try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
+ final File file = getSettingsProblemFile();
+ final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE;
+ try (BufferedReader in = new BufferedReader(new FileReader(file))) {
+ if (skipSize > 0) {
+ in.skip(skipSize);
+ }
String line = null;
while ((line = in.readLine()) != null) {
if (line.contains("ignored: updated version")) continue;
@@ -360,7 +366,12 @@ public class PackageManagerServiceUtils {
}
public static void dumpCriticalInfo(PrintWriter pw, String msg) {
- try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
+ final File file = getSettingsProblemFile();
+ final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE;
+ try (BufferedReader in = new BufferedReader(new FileReader(file))) {
+ if (skipSize > 0) {
+ in.skip(skipSize);
+ }
String line = null;
while ((line = in.readLine()) != null) {
if (line.contains("ignored: updated version")) continue;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c7304c2695c9..cd9a4e72672f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -107,11 +107,10 @@ import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
+import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
-import com.android.server.pm.permission.PermissionSettings;
import com.android.server.utils.TimingsTraceAndSlog;
import libcore.io.IoUtils;
@@ -420,7 +419,7 @@ public final class Settings {
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
/** Settings and other information about permissions */
- final PermissionSettings mPermissions;
+ final LegacyPermissionSettings mPermissions;
private final LegacyPermissionDataProvider mPermissionDataProvider;
@@ -440,11 +439,10 @@ public final class Settings {
mKernelMappingFilename = null;
}
- Settings(File dataDir, PermissionSettings permissionSettings,
- RuntimePermissionsPersistence runtimePermissionsPersistence,
+ Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
LegacyPermissionDataProvider permissionDataProvider, Object lock) {
mLock = lock;
- mPermissions = permissionSettings;
+ mPermissions = new LegacyPermissionSettings(lock);
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
runtimePermissionsPersistence);
mPermissionDataProvider = permissionDataProvider;
@@ -489,10 +487,6 @@ public final class Settings {
mRenamedPackages.remove(pkgName);
}
- public boolean canPropagatePermissionToInstantApp(String permName) {
- return mPermissions.canPropagatePermissionToInstantApp(permName);
- }
-
/** Gets and optionally creates a new shared user id. */
SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags,
boolean create) throws PackageManagerException {
@@ -2128,23 +2122,14 @@ public final class Settings {
String tagName = parser.getName();
if (tagName.equals(TAG_ITEM)) {
String name = parser.getAttributeValue(null, ATTR_NAME);
-
- BasePermission bp = mPermissions.getPermission(name);
- if (bp == null) {
- Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
-
String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
final boolean granted = grantedStr == null
|| Boolean.parseBoolean(grantedStr);
-
String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
-
- permissionsState.putInstallPermissionState(new PermissionState(bp, granted, flags));
+ permissionsState.putInstallPermissionState(new PermissionState(name, granted,
+ flags));
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
+ parser.getName());
@@ -2867,10 +2852,6 @@ public final class Settings {
}
}
- void writePermissionLPr(XmlSerializer serializer, BasePermission bp) throws IOException {
- bp.writeLPr(serializer);
- }
-
boolean readLPw(@NonNull List<UserInfo> users) {
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
@@ -4813,13 +4794,7 @@ public final class Settings {
&& !permissionNames.contains(perm)) {
continue;
}
- pw.print(prefix); pw.print(" "); pw.print(perm);
- final BasePermission bp = mPermissions.getPermission(perm);
- if (bp != null && bp.isHardOrSoftRestricted()) {
- pw.println(": restricted=true");
- } else {
- pw.println();
- }
+ pw.print(prefix); pw.print(" "); pw.println(perm);
}
}
@@ -5024,7 +4999,9 @@ public final class Settings {
void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
DumpState dumpState) {
- mPermissions.dumpPermissions(pw, packageName, permissionNames,
+ LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames,
+ mPermissionDataProvider.getLegacyPermissions(),
+ mPermissionDataProvider.getAllAppOpPermissionPackages(),
(mReadExternalStorageEnforced == Boolean.TRUE), dumpState);
}
@@ -5544,18 +5521,11 @@ public final class Settings {
int permissionsSize = permissions.size();
for (int i = 0; i < permissionsSize; i++) {
RuntimePermissionsState.PermissionState permission = permissions.get(i);
-
String name = permission.getName();
- BasePermission basePermission = mPermissions.getPermission(name);
- if (basePermission == null) {
- Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
- continue;
- }
boolean granted = permission.isGranted();
int flags = permission.getFlags();
-
- permissionsState.putRuntimePermissionState(new PermissionState(basePermission,
- granted, flags), userId);
+ permissionsState.putRuntimePermissionState(new PermissionState(name, granted,
+ flags), userId);
}
}
@@ -5650,23 +5620,14 @@ public final class Settings {
switch (parser.getName()) {
case TAG_ITEM: {
String name = parser.getAttributeValue(null, ATTR_NAME);
- BasePermission bp = mPermissions.getPermission(name);
- if (bp == null) {
- Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
-
String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
final boolean granted = grantedStr == null
|| Boolean.parseBoolean(grantedStr);
-
String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
-
- permissionsState.putRuntimePermissionState(new PermissionState(bp, granted,
- flags), userId);
+ permissionsState.putRuntimePermissionState(new PermissionState(name,
+ granted, flags), userId);
}
break;
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 3bad3cb1d372..e99e301e6da1 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -485,7 +485,8 @@ public class StagingManager {
}
}
- private void resumeSession(@NonNull PackageInstallerSession session) {
+ private void resumeSession(@NonNull PackageInstallerSession session)
+ throws PackageManagerException {
Slog.d(TAG, "Resuming session " + session.sessionId);
final boolean hasApex = session.containsApexSession();
@@ -550,10 +551,8 @@ public class StagingManager {
if (apexSessionInfo == null) {
final String errorMsg = "apexd did not know anything about a staged session "
+ "supposed to be activated";
- session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
- errorMsg);
- abortCheckpoint(session.sessionId, errorMsg);
- return;
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
}
if (isApexSessionFailed(apexSessionInfo)) {
String errorMsg = "APEX activation failed. Check logcat messages from apexd "
@@ -562,10 +561,8 @@ public class StagingManager {
errorMsg = "Session reverted due to crashing native process: "
+ mNativeFailureReason;
}
- session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
- errorMsg);
- abortCheckpoint(session.sessionId, errorMsg);
- return;
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
}
if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
// Apexd did not apply the session for some unknown reason. There is no
@@ -573,43 +570,22 @@ public class StagingManager {
// it as failed.
final String errorMsg = "Staged session " + session.sessionId + "at boot "
+ "didn't activate nor fail. Marking it as failed anyway.";
- session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
- errorMsg);
- abortCheckpoint(session.sessionId, errorMsg);
- return;
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
}
}
- // Handle apk and apk-in-apex installation
- try {
- if (hasApex) {
- checkInstallationOfApkInApexSuccessful(session);
- checkDuplicateApkInApex(session);
- snapshotAndRestoreForApexSession(session);
- Slog.i(TAG, "APEX packages in session " + session.sessionId
- + " were successfully activated. Proceeding with APK packages, if any");
- }
- // The APEX part of the session is activated, proceed with the installation of APKs.
- Slog.d(TAG, "Installing APK packages in session " + session.sessionId);
- installApksInSession(session);
- } catch (PackageManagerException e) {
- session.setStagedSessionFailed(e.error, e.getMessage());
- abortCheckpoint(session.sessionId, e.getMessage());
-
- // If checkpoint is not supported, we have to handle failure for one staged session.
- if (!hasApex) {
- return;
- }
- if (!mApexManager.revertActiveSessions()) {
- Slog.e(TAG, "Failed to abort APEXd session");
- } else {
- Slog.e(TAG,
- "Successfully aborted apexd session. Rebooting device in order to revert "
- + "to the previous state of APEXd.");
- mPowerManager.reboot(null);
- }
- return;
+ // Handle apk and apk-in-apex installation
+ if (hasApex) {
+ checkInstallationOfApkInApexSuccessful(session);
+ checkDuplicateApkInApex(session);
+ snapshotAndRestoreForApexSession(session);
+ Slog.i(TAG, "APEX packages in session " + session.sessionId
+ + " were successfully activated. Proceeding with APK packages, if any");
}
+ // The APEX part of the session is activated, proceed with the installation of APKs.
+ Slog.d(TAG, "Installing APK packages in session " + session.sessionId);
+ installApksInSession(session);
Slog.d(TAG, "Marking session " + session.sessionId + " as applied");
session.setStagedSessionApplied();
@@ -633,6 +609,25 @@ public class StagingManager {
}
}
+ void onInstallationFailure(PackageInstallerSession session, PackageManagerException e) {
+ session.setStagedSessionFailed(e.error, e.getMessage());
+ abortCheckpoint(session.sessionId, e.getMessage());
+
+ // If checkpoint is not supported, we have to handle failure for one staged session.
+ if (!session.containsApexSession()) {
+ return;
+ }
+
+ if (!mApexManager.revertActiveSessions()) {
+ Slog.e(TAG, "Failed to abort APEXd session");
+ } else {
+ Slog.e(TAG,
+ "Successfully aborted apexd session. Rebooting device in order to revert "
+ + "to the previous state of APEXd.");
+ mPowerManager.reboot(null);
+ }
+ }
+
private String getReasonForRevert() {
if (!TextUtils.isEmpty(mFailureReason)) {
return mFailureReason;
@@ -933,7 +928,16 @@ public class StagingManager {
} else {
// Session had already being marked ready. Start the checks to verify if there is any
// follow-up work.
- resumeSession(session);
+ try {
+ resumeSession(session);
+ } catch (PackageManagerException e) {
+ onInstallationFailure(session, e);
+ } catch (Exception e) {
+ final String errorMsg = "Staged install failed due to unhandled exception";
+ Slog.e(TAG, errorMsg, e);
+ onInstallationFailure(session, new PackageManagerException(
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg));
+ }
}
}
@@ -1059,19 +1063,26 @@ public class StagingManager {
onPreRebootVerificationComplete(session);
return;
}
- switch (msg.what) {
- case MSG_PRE_REBOOT_VERIFICATION_START:
- handlePreRebootVerification_Start(session);
- break;
- case MSG_PRE_REBOOT_VERIFICATION_APEX:
- handlePreRebootVerification_Apex(session, rollbackId);
- break;
- case MSG_PRE_REBOOT_VERIFICATION_APK:
- handlePreRebootVerification_Apk(session);
- break;
- case MSG_PRE_REBOOT_VERIFICATION_END:
- handlePreRebootVerification_End(session);
- break;
+ try {
+ switch (msg.what) {
+ case MSG_PRE_REBOOT_VERIFICATION_START:
+ handlePreRebootVerification_Start(session);
+ break;
+ case MSG_PRE_REBOOT_VERIFICATION_APEX:
+ handlePreRebootVerification_Apex(session, rollbackId);
+ break;
+ case MSG_PRE_REBOOT_VERIFICATION_APK:
+ handlePreRebootVerification_Apk(session);
+ break;
+ case MSG_PRE_REBOOT_VERIFICATION_END:
+ handlePreRebootVerification_End(session);
+ break;
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Pre-reboot verification failed due to unhandled exception", e);
+ onPreRebootVerificationFailure(session,
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "Pre-reboot verification failed due to unhandled exception: " + e);
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a0344e27f96c..7f29cd94bfca 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4458,7 +4458,7 @@ public class UserManagerService extends IUserManager.Stub {
}
}
if (userInfo == null) {
- throw new SecurityException("userId can only be the calling user or a managed "
+ throw new SecurityException("userId can only be the calling user or a "
+ "profile associated with this user");
}
return userInfo.creationTime;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index f74913c03ce2..fadd0c850f18 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -727,10 +727,14 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
"compiled_trace.pb");
try {
boolean exists = Files.exists(tracePath);
- Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist"));
+ if (DEBUG) {
+ Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist"));
+ }
if (exists) {
long bytes = Files.size(tracePath);
- Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes));
+ if (DEBUG) {
+ Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes));
+ }
return bytes > 0L;
}
return exists;
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermission.java b/services/core/java/com/android/server/pm/permission/LegacyPermission.java
new file mode 100644
index 000000000000..a19a05ae021c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermission.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.permission;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PermissionInfo;
+import android.util.Log;
+
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerService;
+
+import libcore.util.EmptyArray;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Legacy permission definition.
+ */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public final class LegacyPermission {
+ /**
+ * The permission is defined in a manifest.
+ */
+ public static final int TYPE_MANIFEST = 0;
+
+ /**
+ * The permission is defined in a system config.
+ */
+ public static final int TYPE_CONFIG = 1;
+
+ /**
+ * The permission is defined dynamically.
+ */
+ public static final int TYPE_DYNAMIC = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef({
+ TYPE_MANIFEST,
+ TYPE_CONFIG,
+ TYPE_DYNAMIC,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionType {}
+
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String TAG_ITEM = "item";
+
+ @NonNull
+ private final PermissionInfo mPermissionInfo;
+ @PermissionType
+ private final int mType;
+ private final int mUid;
+ @NonNull
+ private final int[] mGids;
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param permissionInfo the {@link PermissionInfo} for the permission
+ * @param type the type of the permission
+ * @param uid the UID defining the permission
+ * @param gids the GIDs associated with the permission
+ */
+ public LegacyPermission(@NonNull PermissionInfo permissionInfo, @PermissionType int type,
+ int uid, @NonNull int[] gids) {
+ mPermissionInfo = permissionInfo;
+ mType = type;
+ mUid = uid;
+ mGids = gids;
+ }
+
+ private LegacyPermission(@NonNull String name, @NonNull String packageName,
+ @PermissionType int type) {
+ mPermissionInfo = new PermissionInfo();
+ mPermissionInfo.name = name;
+ mPermissionInfo.packageName = packageName;
+ // Default to most conservative protection level.
+ mPermissionInfo.protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ mType = type;
+ mUid = 0;
+ mGids = EmptyArray.INT;
+ }
+
+ /**
+ * Get the {@link PermissionInfo} for this mission.
+ *
+ * @return the {@link PermissionInfo}
+ */
+ @NonNull
+ public PermissionInfo getPermissionInfo() {
+ return mPermissionInfo;
+ }
+
+ /**
+ * Get the type of this mission.
+ *
+ * @return the type
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean read(@NonNull Map<String, LegacyPermission> out,
+ @NonNull XmlPullParser parser) {
+ final String tagName = parser.getName();
+ if (!tagName.equals(TAG_ITEM)) {
+ return false;
+ }
+ final String name = parser.getAttributeValue(null, ATTR_NAME);
+ final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+ final String ptype = parser.getAttributeValue(null, "type");
+ if (name == null || packageName == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: permissions has" + " no name at "
+ + parser.getPositionDescription());
+ return false;
+ }
+ final boolean dynamic = "dynamic".equals(ptype);
+ LegacyPermission bp = out.get(name);
+ // If the permission is builtin, do not clobber it.
+ if (bp == null || bp.mType != TYPE_CONFIG) {
+ bp = new LegacyPermission(name.intern(), packageName,
+ dynamic ? TYPE_DYNAMIC : TYPE_MANIFEST);
+ }
+ bp.mPermissionInfo.protectionLevel = readInt(parser, null, "protection",
+ PermissionInfo.PROTECTION_NORMAL);
+ bp.mPermissionInfo.protectionLevel = PermissionInfo.fixProtectionLevel(
+ bp.mPermissionInfo.protectionLevel);
+ if (dynamic) {
+ bp.mPermissionInfo.icon = readInt(parser, null, "icon", 0);
+ bp.mPermissionInfo.nonLocalizedLabel = parser.getAttributeValue(null, "label");
+ }
+ out.put(bp.mPermissionInfo.name, bp);
+ return true;
+ }
+
+ private static int readInt(@NonNull XmlPullParser parser, @Nullable String namespace,
+ @NonNull String name, int defaultValue) {
+ final String value = parser.getAttributeValue(namespace, name);
+ if (value == null) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: attribute " + name
+ + " has bad integer value " + value + " at "
+ + parser.getPositionDescription());
+ return defaultValue;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void write(@NonNull XmlSerializer serializer) throws IOException {
+ if (mPermissionInfo.packageName == null) {
+ return;
+ }
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, mPermissionInfo.name);
+ serializer.attribute(null, ATTR_PACKAGE, mPermissionInfo.packageName);
+ if (mPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
+ serializer.attribute(null, "protection",
+ Integer.toString(mPermissionInfo.protectionLevel));
+ }
+ if (mType == TYPE_DYNAMIC) {
+ serializer.attribute(null, "type", "dynamic");
+ if (mPermissionInfo.icon != 0) {
+ serializer.attribute(null, "icon", Integer.toString(mPermissionInfo.icon));
+ }
+ if (mPermissionInfo.nonLocalizedLabel != null) {
+ serializer.attribute(null, "label", mPermissionInfo.nonLocalizedLabel.toString());
+ }
+ }
+ serializer.endTag(null, TAG_ITEM);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean dump(@NonNull PrintWriter pw, @NonNull String packageName,
+ @NonNull Set<String> permissionNames, boolean readEnforced, boolean printedSomething,
+ @NonNull DumpState dumpState) {
+ if (packageName != null && !packageName.equals(mPermissionInfo.packageName)) {
+ return false;
+ }
+ if (permissionNames != null && !permissionNames.contains(mPermissionInfo.name)) {
+ return false;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Permissions:");
+ }
+ pw.print(" Permission ["); pw.print(mPermissionInfo.name); pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(this)));
+ pw.println("):");
+ pw.print(" sourcePackage="); pw.println(mPermissionInfo.packageName);
+ pw.print(" uid="); pw.print(mUid);
+ pw.print(" gids="); pw.print(Arrays.toString(mGids));
+ pw.print(" type="); pw.print(mType);
+ pw.print(" prot=");
+ pw.println(PermissionInfo.protectionToString(mPermissionInfo.protectionLevel));
+ if (mPermissionInfo != null) {
+ pw.print(" perm="); pw.println(mPermissionInfo);
+ if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
+ || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+ pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags));
+ }
+ }
+ if (Objects.equals(mPermissionInfo.name,
+ android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
+ pw.print(" enforced=");
+ pw.println(readEnforced);
+ }
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
index 346a2c527fcb..0e790b1899ed 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
@@ -19,12 +19,32 @@ package com.android.server.pm.permission;
import android.annotation.AppIdInt;
import android.annotation.NonNull;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
/**
* An interface for legacy code to read permission data in order to maintain compatibility.
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public interface LegacyPermissionDataProvider {
/**
+ * Get all the legacy permissions currently registered in the system.
+ *
+ * @return the legacy permissions
+ */
+ @NonNull
+ List<LegacyPermission> getLegacyPermissions();
+
+ /**
+ * Get all the package names requesting app op permissions.
+ *
+ * @return a map of app op permission names to package names requesting them
+ */
+ @NonNull
+ Map<String, Set<String>> getAllAppOpPermissionPackages();
+
+ /**
* Get the legacy permission state of an app ID, either a package or a shared user.
*
* @param appId the app ID
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java
new file mode 100644
index 000000000000..cc0b79872ba0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.XmlUtils;
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Legacy permission settings for migration.
+ */
+public class LegacyPermissionSettings {
+ /**
+ * All of the permissions known to the system. The mapping is from permission
+ * name to permission object.
+ */
+ @GuardedBy("mLock")
+ private final ArrayMap<String, LegacyPermission> mPermissions = new ArrayMap<>();
+
+ /**
+ * All permission trees known to the system. The mapping is from permission tree
+ * name to permission object.
+ */
+ @GuardedBy("mLock")
+ private final ArrayMap<String, LegacyPermission> mPermissionTrees = new ArrayMap<>();
+
+ @NonNull
+ private final Object mLock;
+
+ public LegacyPermissionSettings(@NonNull Object lock) {
+ mLock = lock;
+ }
+
+ @NonNull
+ public List<LegacyPermission> getPermissions() {
+ synchronized (mLock) {
+ return new ArrayList<>(mPermissions.values());
+ }
+ }
+
+ @NonNull
+ public List<LegacyPermission> getPermissionTrees() {
+ synchronized (mLock) {
+ return new ArrayList<>(mPermissionTrees.values());
+ }
+ }
+
+ public void replacePermissions(@NonNull List<LegacyPermission> permissions) {
+ synchronized (mLock) {
+ mPermissions.clear();
+ final int permissionsSize = permissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final LegacyPermission permission = permissions.get(i);
+ mPermissions.put(permission.getPermissionInfo().name, permission);
+ }
+ }
+ }
+
+ public void replacePermissionTrees(@NonNull List<LegacyPermission> permissionTrees) {
+ synchronized (mLock) {
+ mPermissionTrees.clear();
+ final int permissionsSize = permissionTrees.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final LegacyPermission permissionTree = permissionTrees.get(i);
+ mPermissionTrees.put(permissionTree.getPermissionInfo().name, permissionTree);
+ }
+ }
+ }
+
+ public void readPermissions(@NonNull XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ synchronized (mLock) {
+ readPermissions(mPermissions, parser);
+ }
+ }
+
+ public void readPermissionTrees(@NonNull XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ synchronized (mLock) {
+ readPermissions(mPermissionTrees, parser);
+ }
+ }
+
+ public static void readPermissions(@NonNull ArrayMap<String, LegacyPermission> out,
+ @NonNull XmlPullParser parser) throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (!LegacyPermission.read(out, parser)) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element reading permissions: " + parser.getName() + " at "
+ + parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ public void writePermissions(@NonNull XmlSerializer serializer) throws IOException {
+ synchronized (mLock) {
+ for (LegacyPermission bp : mPermissions.values()) {
+ bp.write(serializer);
+ }
+ }
+ }
+
+ public void writePermissionTrees(@NonNull XmlSerializer serializer) throws IOException {
+ synchronized (mLock) {
+ for (LegacyPermission bp : mPermissionTrees.values()) {
+ bp.write(serializer);
+ }
+ }
+ }
+
+ public static void dumpPermissions(@NonNull PrintWriter pw, @Nullable String packageName,
+ @Nullable ArraySet<String> permissionNames, @NonNull List<LegacyPermission> permissions,
+ @NonNull Map<String, Set<String>> appOpPermissionPackages,
+ boolean externalStorageEnforced, @NonNull DumpState dumpState) {
+ boolean printedSomething = false;
+ final int permissionsSize = permissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final LegacyPermission permission = permissions.get(i);
+ printedSomething = permission.dump(pw, packageName, permissionNames,
+ externalStorageEnforced, printedSomething, dumpState);
+ }
+ if (packageName == null && permissionNames == null) {
+ boolean firstEntry = true;
+ for (final Map.Entry<String, Set<String>> entry : appOpPermissionPackages.entrySet()) {
+ if (firstEntry) {
+ firstEntry = false;
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("AppOp Permissions:");
+ }
+ pw.print(" AppOp Permission ");
+ pw.print(entry.getKey());
+ pw.println(":");
+ for (final String appOpPackageName : entry.getValue()) {
+ pw.print(" ");
+ pw.println(appOpPackageName);
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
index 63f69cede59c..0e60367c243b 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
@@ -252,7 +252,7 @@ public final class LegacyPermissionState {
*/
public static final class PermissionState {
@NonNull
- private final BasePermission mPermission;
+ private final String mName;
private final boolean mGranted;
@@ -261,40 +261,30 @@ public final class LegacyPermissionState {
/**
* Create a new instance of this class.
*
- * @param permission the {@link BasePermission} for the permission
+ * @param name the name of the permission
* @param granted whether the permission is granted
* @param flags the permission flags
*/
- public PermissionState(@NonNull BasePermission permission, boolean granted, int flags) {
- mPermission = permission;
+ public PermissionState(@NonNull String name, boolean granted, int flags) {
+ mName = name;
mGranted = granted;
mFlags = flags;
}
private PermissionState(@NonNull PermissionState other) {
- mPermission = other.mPermission;
+ mName = other.mName;
mGranted = other.mGranted;
mFlags = other.mFlags;
}
/**
- * Get the {@link BasePermission} for the permission.
- *
- * @return the {@link BasePermission}
- */
- @NonNull
- public BasePermission getPermission() {
- return mPermission;
- }
-
- /**
* Get the permission name.
*
* @return the permission name
*/
@NonNull
public String getName() {
- return mPermission.getName();
+ return mName;
}
/**
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 155d71673e06..c121e6b4a763 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -19,6 +19,7 @@ package com.android.server.pm.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
@@ -28,31 +29,25 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
-import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.util.EmptyArray;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Map;
import java.util.Objects;
-import java.util.Set;
-public final class BasePermission {
- private static final String TAG = "PackageManager";
+/**
+ * Permission definition.
+ */
+public final class Permission {
+ private static final String TAG = "Permission";
- public static final int TYPE_MANIFEST = 0;
- public static final int TYPE_CONFIG = 1;
- public static final int TYPE_DYNAMIC = 2;
+ public static final int TYPE_MANIFEST = LegacyPermission.TYPE_MANIFEST;
+ public static final int TYPE_CONFIG = LegacyPermission.TYPE_CONFIG;
+ public static final int TYPE_DYNAMIC = LegacyPermission.TYPE_DYNAMIC;
@IntDef({
TYPE_MANIFEST,
TYPE_CONFIG,
@@ -70,18 +65,13 @@ public final class BasePermission {
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionLevel {}
- private static final String ATTR_NAME = "name";
- private static final String ATTR_PACKAGE = "package";
- private static final String TAG_ITEM = "item";
-
- private boolean mPermissionDefinitionChanged;
-
@NonNull
private PermissionInfo mPermissionInfo;
private boolean mReconciled;
- private final @PermissionType int mType;
+ @PermissionType
+ private final int mType;
/** UID that owns the definition of this permission */
private int mUid;
@@ -96,7 +86,10 @@ public final class BasePermission {
*/
private boolean mGidsPerUser;
- public BasePermission(@NonNull String name, String packageName, @PermissionType int type) {
+ private boolean mDefinitionChanged;
+
+ public Permission(@NonNull String name, @NonNull String packageName,
+ @PermissionType int type) {
mPermissionInfo = new PermissionInfo();
mPermissionInfo.name = name;
mPermissionInfo.packageName = packageName;
@@ -105,10 +98,27 @@ public final class BasePermission {
mType = type;
}
- @Override
- public String toString() {
- return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " "
- + mPermissionInfo.name + "}";
+ public Permission(@NonNull PermissionInfo permissionInfo, @PermissionType int type) {
+ mPermissionInfo = permissionInfo;
+ mType = type;
+ }
+
+ @NonNull
+ public PermissionInfo getPermissionInfo() {
+ return mPermissionInfo;
+ }
+
+ public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) {
+ if (permissionInfo != null) {
+ mPermissionInfo = permissionInfo;
+ } else {
+ final PermissionInfo newPermissionInfo = new PermissionInfo();
+ newPermissionInfo.name = mPermissionInfo.name;
+ newPermissionInfo.packageName = mPermissionInfo.packageName;
+ newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
+ mPermissionInfo = newPermissionInfo;
+ }
+ mReconciled = permissionInfo != null;
}
@NonNull
@@ -120,14 +130,11 @@ public final class BasePermission {
return mPermissionInfo.protectionLevel;
}
+ @NonNull
public String getPackageName() {
return mPermissionInfo.packageName;
}
- public boolean isPermissionDefinitionChanged() {
- return mPermissionDefinitionChanged;
- }
-
public int getType() {
return mType;
}
@@ -136,34 +143,26 @@ public final class BasePermission {
return mUid;
}
- public void setGids(@NonNull int[] gids, boolean gidsPerUser) {
- mGids = gids;
- mGidsPerUser = gidsPerUser;
+ public boolean hasGids() {
+ return mGids.length != 0;
}
- public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) {
- if (permissionInfo != null) {
- mPermissionInfo = permissionInfo;
- } else {
- final PermissionInfo newPermissionInfo = new PermissionInfo();
- newPermissionInfo.name = mPermissionInfo.name;
- newPermissionInfo.packageName = mPermissionInfo.packageName;
- newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
- mPermissionInfo = newPermissionInfo;
- }
- mReconciled = permissionInfo != null;
+ @NonNull
+ public int[] getRawGids() {
+ return mGids;
}
- public void setPermissionDefinitionChanged(boolean shouldOverride) {
- mPermissionDefinitionChanged = shouldOverride;
+ public boolean areGidsPerUser() {
+ return mGidsPerUser;
}
- public boolean hasGids() {
- return mGids.length != 0;
+ public void setGids(@NonNull int[] gids, boolean gidsPerUser) {
+ mGids = gids;
+ mGidsPerUser = gidsPerUser;
}
@NonNull
- public int[] computeGids(int userId) {
+ public int[] computeGids(@UserIdInt int userId) {
if (mGidsPerUser) {
final int[] userGids = new int[mGids.length];
for (int i = 0; i < mGids.length; i++) {
@@ -176,19 +175,28 @@ public final class BasePermission {
}
}
- public int calculateFootprint(BasePermission perm) {
- if (mUid == perm.mUid) {
- return perm.mPermissionInfo.name.length() + perm.mPermissionInfo.calculateFootprint();
+ public boolean isDefinitionChanged() {
+ return mDefinitionChanged;
+ }
+
+ public void setDefinitionChanged(boolean definitionChanged) {
+ mDefinitionChanged = definitionChanged;
+ }
+
+ public int calculateFootprint(@NonNull Permission permission) {
+ if (mUid == permission.mUid) {
+ return permission.mPermissionInfo.name.length()
+ + permission.mPermissionInfo.calculateFootprint();
}
return 0;
}
- public boolean isPermission(ParsedPermission perm) {
+ public boolean isPermission(@NonNull ParsedPermission parsedPermission) {
if (mPermissionInfo == null) {
return false;
}
- return Objects.equals(mPermissionInfo.packageName, perm.getPackageName())
- && Objects.equals(mPermissionInfo.name, perm.getName());
+ return Objects.equals(mPermissionInfo.packageName, parsedPermission.getPackageName())
+ && Objects.equals(mPermissionInfo.name, parsedPermission.getName());
}
public boolean isDynamic() {
@@ -323,8 +331,8 @@ public final class BasePermission {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
}
- public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
- if (!origPackageName.equals(mPermissionInfo.packageName)) {
+ public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) {
+ if (!oldPackageName.equals(mPermissionInfo.packageName)) {
return;
}
final PermissionInfo newPermissionInfo = new PermissionInfo();
@@ -339,28 +347,29 @@ public final class BasePermission {
}
public boolean addToTree(@ProtectionLevel int protectionLevel,
- @NonNull PermissionInfo permissionInfo, @NonNull BasePermission tree) {
+ @NonNull PermissionInfo permissionInfo, @NonNull Permission permissionTree) {
final boolean changed =
(mPermissionInfo.protectionLevel != protectionLevel
|| !mReconciled
- || mUid != tree.mUid
+ || mUid != permissionTree.mUid
|| !Objects.equals(mPermissionInfo.packageName,
- tree.mPermissionInfo.packageName)
+ permissionTree.mPermissionInfo.packageName)
|| !comparePermissionInfos(mPermissionInfo, permissionInfo));
mPermissionInfo = new PermissionInfo(permissionInfo);
- mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
+ mPermissionInfo.packageName = permissionTree.mPermissionInfo.packageName;
mPermissionInfo.protectionLevel = protectionLevel;
mReconciled = true;
- mUid = tree.mUid;
+ mUid = permissionTree.mUid;
return changed;
}
- public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
- if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
- + getName() + " pkg=" + getPackageName()
- + " info=" + mPermissionInfo);
+ public void updateDynamicPermission(@NonNull Collection<Permission> permissionTrees) {
+ if (PackageManagerService.DEBUG_SETTINGS) {
+ Log.v(TAG, "Dynamic permission: name=" + getName() + " pkg=" + getPackageName()
+ + " info=" + mPermissionInfo);
+ }
if (mType == TYPE_DYNAMIC) {
- final BasePermission tree = findPermissionTree(permissionTrees, mPermissionInfo.name);
+ final Permission tree = findPermissionTree(permissionTrees, mPermissionInfo.name);
if (tree != null) {
mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
mReconciled = true;
@@ -369,19 +378,21 @@ public final class BasePermission {
}
}
- static BasePermission createOrUpdate(PackageManagerInternal packageManagerInternal,
- @Nullable BasePermission bp, @NonNull PermissionInfo p,
- @NonNull AndroidPackage pkg, Collection<BasePermission> permissionTrees,
+ @NonNull
+ static Permission createOrUpdate(PackageManagerInternal packageManagerInternal,
+ @Nullable Permission permission, @NonNull PermissionInfo permissionInfo,
+ @NonNull AndroidPackage pkg, @NonNull Collection<Permission> permissionTrees,
boolean chatty) {
// Allow system apps to redefine non-system permissions
boolean ownerChanged = false;
- if (bp != null && !Objects.equals(bp.mPermissionInfo.packageName, p.packageName)) {
+ if (permission != null && !Objects.equals(permission.mPermissionInfo.packageName,
+ permissionInfo.packageName)) {
final boolean currentOwnerIsSystem;
- if (!bp.mReconciled) {
+ if (!permission.mReconciled) {
currentOwnerIsSystem = false;
} else {
AndroidPackage currentPackage = packageManagerInternal.getPackage(
- bp.mPermissionInfo.packageName);
+ permission.mPermissionInfo.packageName);
if (currentPackage == null) {
currentOwnerIsSystem = false;
} else {
@@ -390,54 +401,56 @@ public final class BasePermission {
}
if (pkg.isSystem()) {
- if (bp.mType == BasePermission.TYPE_CONFIG && !bp.mReconciled) {
+ if (permission.mType == Permission.TYPE_CONFIG && !permission.mReconciled) {
// It's a built-in permission and no owner, take ownership now
- p.flags |= PermissionInfo.FLAG_INSTALLED;
- bp.mPermissionInfo = p;
- bp.mReconciled = true;
- bp.mUid = pkg.getUid();
+ permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED;
+ permission.mPermissionInfo = permissionInfo;
+ permission.mReconciled = true;
+ permission.mUid = pkg.getUid();
} else if (!currentOwnerIsSystem) {
String msg = "New decl " + pkg + " of permission "
- + p.name + " is system; overriding " + bp.mPermissionInfo.packageName;
+ + permissionInfo.name + " is system; overriding "
+ + permission.mPermissionInfo.packageName;
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
ownerChanged = true;
- bp = null;
+ permission = null;
}
}
}
- if (bp == null) {
- bp = new BasePermission(p.name, p.packageName, TYPE_MANIFEST);
+ if (permission == null) {
+ permission = new Permission(permissionInfo.name, permissionInfo.packageName,
+ TYPE_MANIFEST);
}
- boolean wasNormal = bp.isNormal();
+ boolean wasNormal = permission.isNormal();
StringBuilder r = null;
- if (!bp.mReconciled) {
- if (bp.mPermissionInfo.packageName == null
- || bp.mPermissionInfo.packageName.equals(p.packageName)) {
- final BasePermission tree = findPermissionTree(permissionTrees, p.name);
+ if (!permission.mReconciled) {
+ if (permission.mPermissionInfo.packageName == null
+ || permission.mPermissionInfo.packageName.equals(permissionInfo.packageName)) {
+ final Permission tree = findPermissionTree(permissionTrees, permissionInfo.name);
if (tree == null
- || tree.mPermissionInfo.packageName.equals(p.packageName)) {
- p.flags |= PermissionInfo.FLAG_INSTALLED;
- bp.mPermissionInfo = p;
- bp.mReconciled = true;
- bp.mUid = pkg.getUid();
+ || tree.mPermissionInfo.packageName.equals(permissionInfo.packageName)) {
+ permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED;
+ permission.mPermissionInfo = permissionInfo;
+ permission.mReconciled = true;
+ permission.mUid = pkg.getUid();
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
- r.append(p.name);
+ r.append(permissionInfo.name);
}
} else {
- Slog.w(TAG, "Permission " + p.name + " from package "
- + p.packageName + " ignored: base tree "
+ Slog.w(TAG, "Permission " + permissionInfo.name + " from package "
+ + permissionInfo.packageName + " ignored: base tree "
+ tree.mPermissionInfo.name + " is from package "
+ tree.mPermissionInfo.packageName);
}
} else {
- Slog.w(TAG, "Permission " + p.name + " from package "
- + p.packageName + " ignored: original from "
- + bp.mPermissionInfo.packageName);
+ Slog.w(TAG, "Permission " + permissionInfo.name + " from package "
+ + permissionInfo.packageName + " ignored: original from "
+ + permission.mPermissionInfo.packageName);
}
} else if (chatty) {
if (r == null) {
@@ -446,42 +459,47 @@ public final class BasePermission {
r.append(' ');
}
r.append("DUP:");
- r.append(p.name);
+ r.append(permissionInfo.name);
}
- if (bp.isRuntime() && (ownerChanged || wasNormal)) {
+ if (permission.isRuntime() && (ownerChanged || wasNormal)) {
// If this is a runtime permission and the owner has changed, or this was a normal
// permission, then permission state should be cleaned up
- bp.mPermissionDefinitionChanged = true;
+ permission.mDefinitionChanged = true;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
}
- return bp;
+ return permission;
}
- static BasePermission enforcePermissionTree(
- Collection<BasePermission> permissionTrees, String permName, int callingUid) {
- if (permName != null) {
- BasePermission bp = findPermissionTree(permissionTrees, permName);
- if (bp != null) {
- if (bp.mUid == UserHandle.getAppId(callingUid)) {
- return bp;
+ @NonNull
+ public static Permission enforcePermissionTree(@NonNull Collection<Permission> permissionTrees,
+ @NonNull String permissionName, int callingUid) {
+ if (permissionName != null) {
+ final Permission permissionTree = Permission.findPermissionTree(permissionTrees,
+ permissionName);
+ if (permissionTree != null) {
+ if (permissionTree.getUid() == UserHandle.getAppId(callingUid)) {
+ return permissionTree;
}
throw new SecurityException("Calling uid " + callingUid
+ " is not allowed to add to permission tree "
- + bp.mPermissionInfo.name + " owned by uid " + bp.mUid);
+ + permissionTree.getName() + " owned by uid "
+ + permissionTree.getUid());
}
}
- throw new SecurityException("No permission tree found for " + permName);
+ throw new SecurityException("No permission tree found for " + permissionName);
}
- private static BasePermission findPermissionTree(
- Collection<BasePermission> permissionTrees, String permName) {
- for (BasePermission bp : permissionTrees) {
- if (permName.startsWith(bp.mPermissionInfo.name)
- && permName.length() > bp.mPermissionInfo.name.length()
- && permName.charAt(bp.mPermissionInfo.name.length()) == '.') {
- return bp;
+ @Nullable
+ private static Permission findPermissionTree(@NonNull Collection<Permission> permissionTrees,
+ @NonNull String permissionName) {
+ for (final Permission permissionTree : permissionTrees) {
+ final String permissionTreeName = permissionTree.getName();
+ if (permissionName.startsWith(permissionTreeName)
+ && permissionName.length() > permissionTreeName.length()
+ && permissionName.charAt(permissionTreeName.length()) == '.') {
+ return permissionTree;
}
}
return null;
@@ -512,7 +530,7 @@ public final class BasePermission {
@NonNull
public PermissionInfo generatePermissionInfo(int flags, int targetSdkVersion) {
- PermissionInfo permissionInfo;
+ final PermissionInfo permissionInfo;
if (mPermissionInfo != null) {
permissionInfo = new PermissionInfo(mPermissionInfo);
if ((flags & PackageManager.GET_META_DATA) != PackageManager.GET_META_DATA) {
@@ -539,79 +557,6 @@ public final class BasePermission {
return permissionInfo;
}
- public static boolean readLPw(@NonNull Map<String, BasePermission> out,
- @NonNull XmlPullParser parser) {
- final String tagName = parser.getName();
- if (!tagName.equals(TAG_ITEM)) {
- return false;
- }
- final String name = parser.getAttributeValue(null, ATTR_NAME);
- final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
- final String ptype = parser.getAttributeValue(null, "type");
- if (name == null || packageName == null) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: permissions has" + " no name at "
- + parser.getPositionDescription());
- return false;
- }
- final boolean dynamic = "dynamic".equals(ptype);
- BasePermission bp = out.get(name);
- // If the permission is builtin, do not clobber it.
- if (bp == null || bp.mType != TYPE_CONFIG) {
- bp = new BasePermission(name.intern(), packageName,
- dynamic ? TYPE_DYNAMIC : TYPE_MANIFEST);
- }
- bp.mPermissionInfo.protectionLevel = readInt(parser, null, "protection",
- PermissionInfo.PROTECTION_NORMAL);
- bp.mPermissionInfo.protectionLevel = PermissionInfo.fixProtectionLevel(
- bp.mPermissionInfo.protectionLevel);
- if (dynamic) {
- bp.mPermissionInfo.icon = readInt(parser, null, "icon", 0);
- bp.mPermissionInfo.nonLocalizedLabel = parser.getAttributeValue(null, "label");
- }
- out.put(bp.mPermissionInfo.name, bp);
- return true;
- }
-
- private static int readInt(XmlPullParser parser, String ns, String name, int defValue) {
- String v = parser.getAttributeValue(ns, name);
- try {
- if (v == null) {
- return defValue;
- }
- return Integer.parseInt(v);
- } catch (NumberFormatException e) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: attribute " + name
- + " has bad integer value " + v + " at "
- + parser.getPositionDescription());
- }
- return defValue;
- }
-
- public void writeLPr(@NonNull XmlSerializer serializer) throws IOException {
- if (mPermissionInfo.packageName == null) {
- return;
- }
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, mPermissionInfo.name);
- serializer.attribute(null, ATTR_PACKAGE, mPermissionInfo.packageName);
- if (mPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
- serializer.attribute(null, "protection",
- Integer.toString(mPermissionInfo.protectionLevel));
- }
- if (mType == TYPE_DYNAMIC) {
- serializer.attribute(null, "type", "dynamic");
- if (mPermissionInfo.icon != 0) {
- serializer.attribute(null, "icon", Integer.toString(mPermissionInfo.icon));
- }
- if (mPermissionInfo.nonLocalizedLabel != null) {
- serializer.attribute(null, "label", mPermissionInfo.nonLocalizedLabel.toString());
- }
- }
- serializer.endTag(null, TAG_ITEM);
- }
-
private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
if (pi1.icon != pi2.icon) return false;
if (pi1.logo != pi2.logo) return false;
@@ -627,42 +572,4 @@ public final class BasePermission {
//if (pi1.descriptionRes != pi2.descriptionRes) return false;
return true;
}
-
- public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName,
- @NonNull Set<String> permissionNames, boolean readEnforced,
- boolean printedSomething, @NonNull DumpState dumpState) {
- if (packageName != null && !packageName.equals(mPermissionInfo.packageName)) {
- return false;
- }
- if (permissionNames != null && !permissionNames.contains(mPermissionInfo.name)) {
- return false;
- }
- if (!printedSomething) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Permissions:");
- }
- pw.print(" Permission ["); pw.print(mPermissionInfo.name); pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(this)));
- pw.println("):");
- pw.print(" sourcePackage="); pw.println(mPermissionInfo.packageName);
- pw.print(" uid="); pw.print(mUid);
- pw.print(" gids="); pw.print(Arrays.toString(computeGids(UserHandle.USER_SYSTEM)));
- pw.print(" type="); pw.print(mType);
- pw.print(" prot=");
- pw.println(PermissionInfo.protectionToString(mPermissionInfo.protectionLevel));
- if (mPermissionInfo != null) {
- pw.print(" perm="); pw.println(mPermissionInfo);
- if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
- || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
- pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags));
- }
- }
- if (Objects.equals(mPermissionInfo.name,
- android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
- pw.print(" enforced=");
- pw.println(readEnforced);
- }
- return true;
- }
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index da4ef63d6945..6d987aee8995 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -252,7 +252,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
/** Internal storage for permissions and related settings */
@GuardedBy("mLock")
- private final PermissionSettings mSettings;
+ private final PermissionRegistry mRegistry;
/** Injector that can be used to facilitate testing. */
private final Injector mInjector;
@@ -387,7 +387,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mLock = externalLock;
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
- mSettings = new PermissionSettings(mLock);
+ mRegistry = new PermissionRegistry(mLock);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mHandlerThread = new ServiceThread(TAG,
@@ -409,10 +409,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
synchronized (mLock) {
for (int i=0; i<permConfig.size(); i++) {
final SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
- BasePermission bp = mSettings.getPermissionLocked(perm.name);
+ Permission bp = mRegistry.getPermissionLocked(perm.name);
if (bp == null) {
- bp = new BasePermission(perm.name, "android", BasePermission.TYPE_CONFIG);
- mSettings.putPermissionLocked(perm.name, bp);
+ bp = new Permission(perm.name, "android", Permission.TYPE_CONFIG);
+ mRegistry.addPermissionLocked(bp);
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
@@ -485,9 +485,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Nullable
- BasePermission getPermission(String permName) {
+ Permission getPermission(String permName) {
synchronized (mLock) {
- return mSettings.getPermissionLocked(permName);
+ return mRegistry.getPermissionLocked(permName);
}
}
@@ -501,7 +501,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return null;
}
synchronized (mLock) {
- final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName);
+ final ArraySet<String> pkgs = mRegistry.getAppOpPermissionPackagesLocked(permName);
if (pkgs == null) {
return null;
}
@@ -518,9 +518,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return ParceledListSlice.emptyList();
}
synchronized (mLock) {
- final int n = mSettings.mPermissionGroups.size();
- final ArrayList<PermissionGroupInfo> out = new ArrayList<>(n);
- for (ParsedPermissionGroup pg : mSettings.mPermissionGroups.values()) {
+ final List<PermissionGroupInfo> out = new ArrayList<>();
+ for (ParsedPermissionGroup pg : mRegistry.getPermissionGroupsLocked()) {
out.add(PackageInfoUtils.generatePermissionGroupInfo(pg, flags));
}
return new ParceledListSlice<>(out);
@@ -538,7 +537,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
synchronized (mLock) {
return PackageInfoUtils.generatePermissionGroupInfo(
- mSettings.mPermissionGroups.get(groupName), flags);
+ mRegistry.getPermissionGroupLocked(groupName), flags);
}
}
@@ -555,7 +554,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int targetSdkVersion = getPermissionInfoCallingTargetSdkVersion(opPackage,
callingUid);
synchronized (mLock) {
- final BasePermission bp = mSettings.getPermissionLocked(permName);
+ final Permission bp = mRegistry.getPermissionLocked(permName);
if (bp == null) {
return null;
}
@@ -585,11 +584,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return null;
}
synchronized (mLock) {
- if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) {
+ if (groupName != null && mRegistry.getPermissionGroupLocked(groupName) == null) {
return null;
}
final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
- for (BasePermission bp : mSettings.mPermissions.values()) {
+ for (Permission bp : mRegistry.getPermissionsLocked()) {
if (Objects.equals(bp.getGroup(), groupName)) {
out.add(bp.generatePermissionInfo(flags));
}
@@ -607,24 +606,24 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
throw new SecurityException("Label must be specified in permission");
}
- final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid);
+ final Permission tree = mRegistry.enforcePermissionTree(info.name, callingUid);
final boolean added;
final boolean changed;
synchronized (mLock) {
- BasePermission bp = mSettings.getPermissionLocked(info.name);
+ Permission bp = mRegistry.getPermissionLocked(info.name);
added = bp == null;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
if (added) {
enforcePermissionCapLocked(info, tree);
- bp = new BasePermission(info.name, tree.getPackageName(),
- BasePermission.TYPE_DYNAMIC);
+ bp = new Permission(info.name, tree.getPackageName(),
+ Permission.TYPE_DYNAMIC);
} else if (!bp.isDynamic()) {
throw new SecurityException("Not allowed to modify non-dynamic permission "
+ info.name);
}
changed = bp.addToTree(fixedLevel, info, tree);
if (added) {
- mSettings.putPermissionLocked(info.name, bp);
+ mRegistry.addPermissionLocked(bp);
}
}
if (changed) {
@@ -639,9 +638,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
- final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid);
+ final Permission tree = mRegistry.enforcePermissionTree(permName, callingUid);
synchronized (mLock) {
- final BasePermission bp = mSettings.getPermissionLocked(permName);
+ final Permission bp = mRegistry.getPermissionLocked(permName);
if (bp == null) {
return;
}
@@ -650,7 +649,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
Slog.wtf(TAG, "Not allowed to modify non-dynamic permission "
+ permName);
}
- mSettings.removePermissionLocked(permName);
+ mRegistry.removePermissionLocked(permName);
mPackageManagerInt.writeSettings(false);
}
}
@@ -683,7 +682,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
synchronized (mLock) {
- if (mSettings.getPermissionLocked(permName) == null) {
+ if (mRegistry.getPermissionLocked(permName) == null) {
return 0;
}
@@ -808,10 +807,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- final BasePermission bp;
+ final Permission bp;
final boolean permissionUpdated;
synchronized (mLock) {
- bp = mSettings.getPermissionLocked(permName);
+ bp = mRegistry.getPermissionLocked(permName);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
@@ -970,7 +969,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
if (isInstantApp) {
- return mSettings.isPermissionInstant(permissionName);
+ return mRegistry.isPermissionInstant(permissionName);
}
return true;
@@ -1232,7 +1231,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(
@NonNull String permName) {
synchronized (mLock) {
- final BasePermission bp = mSettings.getPermissionLocked(permName);
+ final Permission bp = mRegistry.getPermissionLocked(permName);
if (bp == null) {
Slog.w(TAG, "No such permissions: " + permName);
return false;
@@ -1473,9 +1472,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final BasePermission bp;
+ final Permission bp;
synchronized (mLock) {
- bp = mSettings.getPermissionLocked(permName);
+ bp = mRegistry.getPermissionLocked(permName);
}
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + permName);
@@ -1627,7 +1626,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final BasePermission bp = mSettings.getPermission(permName);
+ final Permission bp = mRegistry.getPermission(permName);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
@@ -1808,9 +1807,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int i = 0; i < permissionCount; i++) {
final String permName = pkg.getRequestedPermissions().get(i);
- final BasePermission bp;
+ final Permission bp;
synchronized (mLock) {
- bp = mSettings.getPermissionLocked(permName);
+ bp = mRegistry.getPermissionLocked(permName);
}
if (bp == null) {
continue;
@@ -2093,7 +2092,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return false;
}
- BasePermission permission = getPermission(permName);
+ Permission permission = getPermission(permName);
if (permission == null) {
return false;
}
@@ -2348,7 +2347,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int permNum = 0; permNum < numPermissions; permNum++) {
String permName = permissionsToRevoke.get(permNum);
- BasePermission bp = mSettings.getPermission(permName);
+ Permission bp = mRegistry.getPermission(permName);
if (bp == null || !bp.isRuntime()) {
continue;
}
@@ -2388,7 +2387,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
}
- bp.setPermissionDefinitionChanged(false);
+ bp.setDefinitionChanged(false);
}
}
@@ -2407,7 +2406,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// permissions for one app being granted to someone just because they happen
// to be in a group defined by another app (before this had no implications).
if (pkg.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) {
- p.setParsedPermissionGroup(mSettings.mPermissionGroups.get(p.getGroup()));
+ p.setParsedPermissionGroup(mRegistry.getPermissionGroupLocked(p.getGroup()));
// Warn for a permission in an unknown group.
if (DEBUG_PERMISSIONS
&& p.getGroup() != null && p.getParsedPermissionGroup() == null) {
@@ -2418,24 +2417,24 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final PermissionInfo permissionInfo = PackageInfoUtils.generatePermissionInfo(p,
PackageManager.GET_META_DATA);
- final BasePermission bp;
+ final Permission bp;
if (p.isTree()) {
- bp = BasePermission.createOrUpdate(
+ bp = Permission.createOrUpdate(
mPackageManagerInt,
- mSettings.getPermissionTreeLocked(p.getName()), permissionInfo, pkg,
- mSettings.getAllPermissionTreesLocked(), chatty);
- mSettings.putPermissionTreeLocked(p.getName(), bp);
+ mRegistry.getPermissionTreeLocked(p.getName()), permissionInfo, pkg,
+ mRegistry.getPermissionTreesLocked(), chatty);
+ mRegistry.addPermissionTreeLocked(bp);
} else {
- bp = BasePermission.createOrUpdate(
+ bp = Permission.createOrUpdate(
mPackageManagerInt,
- mSettings.getPermissionLocked(p.getName()),
- permissionInfo, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
- mSettings.putPermissionLocked(p.getName(), bp);
+ mRegistry.getPermissionLocked(p.getName()),
+ permissionInfo, pkg, mRegistry.getPermissionTreesLocked(), chatty);
+ mRegistry.addPermissionLocked(bp);
}
if (bp.isInstalled()) {
p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
}
- if (bp.isPermissionDefinitionChanged()) {
+ if (bp.isDefinitionChanged()) {
definitionChangedPermissions.add(p.getName());
}
}
@@ -2444,45 +2443,46 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) {
- final int N = ArrayUtils.size(pkg.getPermissionGroups());
- StringBuilder r = null;
- for (int i=0; i<N; i++) {
- final ParsedPermissionGroup pg = pkg.getPermissionGroups().get(i);
- final ParsedPermissionGroup cur = mSettings.mPermissionGroups.get(pg.getName());
- final String curPackageName = (cur == null) ? null : cur.getPackageName();
- final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName);
- if (cur == null || isPackageUpdate) {
- mSettings.mPermissionGroups.put(pg.getName(), pg);
- if (chatty && DEBUG_PACKAGE_SCANNING) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- if (isPackageUpdate) {
- r.append("UPD:");
+ synchronized (mLock) {
+ final int N = ArrayUtils.size(pkg.getPermissionGroups());
+ StringBuilder r = null;
+ for (int i = 0; i < N; i++) {
+ final ParsedPermissionGroup pg = pkg.getPermissionGroups().get(i);
+ final ParsedPermissionGroup cur = mRegistry.getPermissionGroupLocked(pg.getName());
+ final String curPackageName = (cur == null) ? null : cur.getPackageName();
+ final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName);
+ if (cur == null || isPackageUpdate) {
+ mRegistry.addPermissionGroupLocked(pg);
+ if (chatty && DEBUG_PACKAGE_SCANNING) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ if (isPackageUpdate) {
+ r.append("UPD:");
+ }
+ r.append(pg.getName());
}
- r.append(pg.getName());
- }
- } else {
- Slog.w(TAG, "Permission group " + pg.getName() + " from package "
- + pg.getPackageName() + " ignored: original from "
- + cur.getPackageName());
- if (chatty && DEBUG_PACKAGE_SCANNING) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
+ } else {
+ Slog.w(TAG, "Permission group " + pg.getName() + " from package "
+ + pg.getPackageName() + " ignored: original from "
+ + cur.getPackageName());
+ if (chatty && DEBUG_PACKAGE_SCANNING) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append("DUP:");
+ r.append(pg.getName());
}
- r.append("DUP:");
- r.append(pg.getName());
}
}
+ if (r != null && DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, " Permission Groups: " + r);
+ }
}
- if (r != null && DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, " Permission Groups: " + r);
- }
-
}
private void removeAllPermissions(AndroidPackage pkg, boolean chatty) {
@@ -2491,9 +2491,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
StringBuilder r = null;
for (int i=0; i<N; i++) {
ParsedPermission p = pkg.getPermissions().get(i);
- BasePermission bp = mSettings.mPermissions.get(p.getName());
+ Permission bp = mRegistry.getPermissionLocked(p.getName());
if (bp == null) {
- bp = mSettings.mPermissionTrees.get(p.getName());
+ bp = mRegistry.getPermissionTreeLocked(p.getName());
}
if (bp != null && bp.isPermission(p)) {
bp.setPermissionInfo(null);
@@ -2507,11 +2507,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
if (p.isAppOp()) {
- ArraySet<String> appOpPkgs =
- mSettings.mAppOpPermissionPackages.get(p.getName());
- if (appOpPkgs != null) {
- appOpPkgs.remove(pkg.getPackageName());
- }
+ // TODO(zhanghai): Should we just remove the entry for this permission directly?
+ mRegistry.removeAppOpPermissionPackageLocked(p.getName(), pkg.getPackageName());
}
}
if (r != null) {
@@ -2522,14 +2519,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
r = null;
for (int i=0; i<N; i++) {
String perm = pkg.getRequestedPermissions().get(i);
- if (mSettings.isPermissionAppOp(perm)) {
- ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm);
- if (appOpPkgs != null) {
- appOpPkgs.remove(pkg.getPackageName());
- if (appOpPkgs.isEmpty()) {
- mSettings.mAppOpPermissionPackages.remove(perm);
- }
- }
+ if (mRegistry.isPermissionAppOp(perm)) {
+ mRegistry.removeAppOpPermissionPackageLocked(perm, pkg.getPackageName());
}
}
if (r != null) {
@@ -2567,7 +2558,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final Set<String> instantPermissions =
new ArraySet<>(uidState.getGrantedPermissions());
instantPermissions.removeIf(permissionName -> {
- BasePermission permission = mSettings.getPermission(permissionName);
+ Permission permission = mRegistry.getPermission(permissionName);
if (permission == null) {
return true;
}
@@ -2585,7 +2576,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@NonNull
private int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) {
- BasePermission permission = mSettings.getPermission(permissionName);
+ Permission permission = mRegistry.getPermission(permissionName);
if (permission == null) {
return EmptyArray.INT;
}
@@ -2638,7 +2629,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int i = 0; i < requestedPermissionsSize; i++) {
final String permissionName = pkg.getRequestedPermissions().get(i);
- final BasePermission permission = mSettings.getPermission(permissionName);
+ final Permission permission = mRegistry.getPermission(permissionName);
if (permission == null) {
continue;
}
@@ -2683,7 +2674,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
for (String permissionName : uidRequestedPermissions) {
- BasePermission permission = mSettings.getPermission(permissionName);
+ Permission permission = mRegistry.getPermission(permissionName);
if (permission == null) {
continue;
}
@@ -2741,7 +2732,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int i = 0; i < requestedPermissionsSize; i++) {
final String permName = requestedPermissions.get(i);
- final BasePermission bp = mSettings.getPermission(permName);
+ final Permission bp = mRegistry.getPermission(permName);
final boolean appSupportsRuntimePermissions =
pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
String legacyActivityRecognitionPermission = null;
@@ -2834,7 +2825,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Keep track of app op permissions.
if (bp.isAppOp()) {
- mSettings.addAppOpPackage(perm, pkg.getPackageName());
+ mRegistry.addAppOpPermissionPackageLocked(perm, pkg.getPackageName());
}
boolean shouldGrantNormalPermission = true;
@@ -3057,7 +3048,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (String permission : ps.getGrantedPermissions()) {
if (!pkg.getImplicitPermissions().contains(permission)) {
- BasePermission bp = mSettings.getPermissionLocked(permission);
+ Permission bp = mRegistry.getPermissionLocked(permission);
if (bp != null && bp.isRuntime()) {
int flags = ps.getPermissionFlags(permission);
@@ -3131,11 +3122,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
+ " for " + pkgName);
}
- ps.grantPermission(mSettings.getPermissionLocked(newPerm));
+ ps.grantPermission(mRegistry.getPermissionLocked(newPerm));
}
// Add permission flags
- ps.updatePermissionFlags(mSettings.getPermission(newPerm), flags, flags);
+ ps.updatePermissionFlags(mRegistry.getPermission(newPerm), flags, flags);
}
/**
@@ -3206,7 +3197,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
ArraySet<String> sourcePerms = newToSplitPerms.get(newPerm);
if (sourcePerms != null) {
- BasePermission bp = mSettings.getPermissionLocked(newPerm);
+ Permission bp = mRegistry.getPermissionLocked(newPerm);
if (bp.isRuntime()) {
if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
@@ -3221,7 +3212,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
sourcePermNum++) {
final String sourcePerm = sourcePerms.valueAt(sourcePermNum);
- BasePermission sourceBp = mSettings.getPermissionLocked(sourcePerm);
+ Permission sourceBp = mRegistry.getPermissionLocked(sourcePerm);
if (!sourceBp.isRuntime()) {
inheritsFromInstallPerm = true;
break;
@@ -3344,7 +3335,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private boolean shouldGrantSignaturePermission(@NonNull AndroidPackage pkg,
- @NonNull PackageSetting pkgSetting, @NonNull BasePermission bp) {
+ @NonNull PackageSetting pkgSetting, @NonNull Permission bp) {
// expect single system package
String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM));
@@ -3510,7 +3501,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@NonNull
private PackageParser.SigningDetails getSourcePackageSigningDetails(
- @NonNull BasePermission bp) {
+ @NonNull Permission bp) {
final PackageSetting ps = getSourcePackageSetting(bp);
if (ps == null) {
return PackageParser.SigningDetails.UNKNOWN;
@@ -3519,13 +3510,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Nullable
- private PackageSetting getSourcePackageSetting(@NonNull BasePermission bp) {
+ private PackageSetting getSourcePackageSetting(@NonNull Permission bp) {
final String sourcePackageName = bp.getPackageName();
return mPackageManagerInt.getPackageSetting(sourcePackageName);
}
private boolean canGrantPrivilegedPermission(@NonNull AndroidPackage pkg,
- boolean isUpdatedSystemApp, @NonNull BasePermission permission) {
+ boolean isUpdatedSystemApp, @NonNull Permission permission) {
if (!pkg.isPrivileged()) {
return false;
}
@@ -3671,9 +3662,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.getPackageName(), userId);
for (String permission : pkg.getRequestedPermissions()) {
- final BasePermission bp;
+ final Permission bp;
synchronized (mLock) {
- bp = mSettings.getPermissionLocked(permission);
+ bp = mRegistry.getPermissionLocked(permission);
}
if (bp != null && (bp.isRuntime() || bp.isDevelopment())
&& (!instantApp || bp.isInstant())
@@ -3713,7 +3704,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int j = 0; j < permissionCount; j++) {
final String permissionName = pkg.getRequestedPermissions().get(j);
- final BasePermission bp = mSettings.getPermissionLocked(permissionName);
+ final Permission bp = mRegistry.getPermissionLocked(permissionName);
if (bp == null || !bp.isHardOrSoftRestricted()) {
continue;
@@ -3874,7 +3865,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
int affectedUserId = UserHandle.USER_NULL;
// Update permissions
for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) {
- BasePermission bp = mSettings.getPermission(eachPerm);
+ Permission bp = mRegistry.getPermission(eachPerm);
if (bp == null) {
continue;
}
@@ -3948,7 +3939,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int requestedPermCount = pkg.getRequestedPermissions().size();
for (int j = 0; j < requestedPermCount; j++) {
String permission = pkg.getRequestedPermissions().get(j);
- BasePermission bp = mSettings.getPermissionLocked(permission);
+ Permission bp = mRegistry.getPermissionLocked(permission);
if (bp != null) {
usedPermissions.add(permission);
}
@@ -3963,7 +3954,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int i = permissionStatesSize - 1; i >= 0; i--) {
PermissionState permissionState = permissionStates.get(i);
if (!usedPermissions.contains(permissionState.getName())) {
- BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
+ Permission bp = mRegistry.getPermissionLocked(permissionState.getName());
if (bp != null) {
if (uidState.removePermissionState(bp.getName())
&& permissionState.isRuntime()) {
@@ -4036,7 +4027,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Cache background -> foreground permission mapping.
// Only system declares background permissions, hence mapping does never change.
mBackgroundPermissions = new ArrayMap<>();
- for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
+ for (Permission bp : mRegistry.getPermissionsLocked()) {
if (bp.getBackgroundPermission() != null) {
String fgPerm = bp.getName();
String bgPerm = bp.getBackgroundPermission();
@@ -4180,13 +4171,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
boolean changed = false;
- Set<BasePermission> needsUpdate = null;
+ Set<Permission> needsUpdate = null;
synchronized (mLock) {
- final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
- while (it.hasNext()) {
- final BasePermission bp = it.next();
+ for (final Permission bp : mRegistry.getPermissionsLocked()) {
if (bp.isDynamic()) {
- bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
+ bp.updateDynamicPermission(mRegistry.getPermissionTreesLocked());
}
if (!packageName.equals(bp.getPackageName())) {
// Not checking sourcePackageSetting because it can be null when
@@ -4198,13 +4187,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Set to changed for either install or uninstall
changed = true;
if (needsUpdate == null) {
- needsUpdate = new ArraySet<>(mSettings.mPermissions.size());
+ needsUpdate = new ArraySet<>();
}
needsUpdate.add(bp);
}
}
if (needsUpdate != null) {
- for (final BasePermission bp : needsUpdate) {
+ for (final Permission bp : needsUpdate) {
// If the target package is being uninstalled, we need to revoke this permission
// From all other packages
if (pkg == null || !hasPermission(pkg, bp.getName())) {
@@ -4236,7 +4225,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
});
}
- mSettings.removePermissionLocked(bp.getName());
+ mRegistry.removePermissionLocked(bp.getName());
continue;
}
final AndroidPackage sourcePkg =
@@ -4250,7 +4239,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
Slog.w(TAG, "Removing dangling permission: " + bp.getName()
+ " from package " + bp.getPackageName());
- mSettings.removePermissionLocked(bp.getName());
+ mRegistry.removePermissionLocked(bp.getName());
}
}
}
@@ -4316,11 +4305,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
boolean changed = false;
- Set<BasePermission> needsUpdate = null;
+ Set<Permission> needsUpdate = null;
synchronized (mLock) {
- final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
+ final Iterator<Permission> it = mRegistry.getPermissionTreesLocked().iterator();
while (it.hasNext()) {
- final BasePermission bp = it.next();
+ final Permission bp = it.next();
if (!packageName.equals(bp.getPackageName())) {
// Not checking sourcePackageSetting because it can be null when
// the permission source package is the target package and the target package is
@@ -4336,13 +4325,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
it.remove();
}
if (needsUpdate == null) {
- needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size());
+ needsUpdate = new ArraySet<>();
}
needsUpdate.add(bp);
}
}
if (needsUpdate != null) {
- for (final BasePermission bp : needsUpdate) {
+ for (final Permission bp : needsUpdate) {
final AndroidPackage sourcePkg =
mPackageManagerInt.getPackage(bp.getPackageName());
final PackageSetting sourcePs =
@@ -4354,7 +4343,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
+ " from package " + bp.getPackageName());
- mSettings.removePermissionLocked(bp.getName());
+ mRegistry.removePermissionLocked(bp.getName());
}
}
}
@@ -4536,16 +4525,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@GuardedBy({"mSettings.mLock", "mLock"})
- private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
+ private int calculateCurrentPermissionFootprintLocked(@NonNull Permission permissionTree) {
int size = 0;
- for (BasePermission perm : mSettings.mPermissions.values()) {
- size += tree.calculateFootprint(perm);
+ for (final Permission permission : mRegistry.getPermissionsLocked()) {
+ size += permissionTree.calculateFootprint(permission);
}
return size;
}
@GuardedBy({"mSettings.mLock", "mLock"})
- private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
+ private void enforcePermissionCapLocked(PermissionInfo info, Permission tree) {
// We calculate the max size of permissions defined by this uid and throw
// if that plus the size of 'info' would exceed our stated maximum.
if (tree.getUid() != Process.SYSTEM_UID) {
@@ -4670,7 +4659,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- private void readStateFromPackageSettings() {
+ private void readLegacyPermissionState() {
final int[] userIds = getAllUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
final int appId = ps.getAppId();
@@ -4684,24 +4673,30 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final UidPermissionState uidState = userState.getOrCreateUidState(appId);
uidState.reset();
uidState.setMissing(legacyState.isMissing(userId));
- readStateFromPermissionStates(uidState,
+ readLegacyPermissionStatesLocked(uidState,
legacyState.getInstallPermissionStates());
- readStateFromPermissionStates(uidState,
+ readLegacyPermissionStatesLocked(uidState,
legacyState.getRuntimePermissionStates(userId));
}
}
});
}
- private void readStateFromPermissionStates(@NonNull UidPermissionState uidState,
+ private void readLegacyPermissionStatesLocked(@NonNull UidPermissionState uidState,
@NonNull Collection<LegacyPermissionState.PermissionState> permissionStates) {
for (final LegacyPermissionState.PermissionState permissionState : permissionStates) {
- uidState.putPermissionState(permissionState.getPermission(),
- permissionState.isGranted(), permissionState.getFlags());
+ final String permissionName = permissionState.getName();
+ final Permission permission = mRegistry.getPermissionLocked(permissionName);
+ if (permission == null) {
+ Slog.w(TAG, "Unknown permission: " + permissionName);
+ continue;
+ }
+ uidState.putPermissionState(permission, permissionState.isGranted(),
+ permissionState.getFlags());
}
}
- private void writeStateToPackageSettings() {
+ private void writeLegacyPermissionState() {
final int[] userIds;
synchronized (mLock) {
userIds = mState.getUserIds();
@@ -4738,12 +4733,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final PermissionState permissionState = permissionStates.get(i);
final LegacyPermissionState.PermissionState legacyPermissionState =
- new LegacyPermissionState.PermissionState(
- permissionState.getPermission(),
+ new LegacyPermissionState.PermissionState(permissionState.getName(),
permissionState.isGranted(), permissionState.getFlags());
if (permissionState.isRuntime()) {
- legacyState.putRuntimePermissionState(legacyPermissionState,
- userId);
+ legacyState.putRuntimePermissionState(legacyPermissionState, userId);
} else {
legacyState.putInstallPermissionState(legacyPermissionState);
}
@@ -4753,6 +4746,108 @@ public class PermissionManagerService extends IPermissionManager.Stub {
});
}
+ private void readLegacyPermissions(@NonNull LegacyPermissionSettings legacyPermissionSettings) {
+ for (int readPermissionOrPermissionTree = 0; readPermissionOrPermissionTree < 2;
+ readPermissionOrPermissionTree++) {
+ final List<LegacyPermission> legacyPermissions = readPermissionOrPermissionTree == 0
+ ? legacyPermissionSettings.getPermissions()
+ : legacyPermissionSettings.getPermissionTrees();
+ synchronized (mLock) {
+ final int legacyPermissionsSize = legacyPermissions.size();
+ for (int i = 0; i < legacyPermissionsSize; i++) {
+ final LegacyPermission legacyPermission = legacyPermissions.get(i);
+ final Permission permission = new Permission(
+ legacyPermission.getPermissionInfo(), legacyPermission.getType());
+ if (readPermissionOrPermissionTree == 0) {
+ // Config permissions are currently read in PermissionManagerService
+ // constructor. The old behavior was to add other attributes to the config
+ // permission in LegacyPermission.read(), so equivalently we can add the
+ // GIDs to the new permissions here, since config permissions created in
+ // PermissionManagerService constructor get only their names and GIDs there.
+ final Permission configPermission = mRegistry.getPermission(
+ permission.getName());
+ if (configPermission != null
+ && configPermission.getType() == Permission.TYPE_CONFIG) {
+ permission.setGids(configPermission.getRawGids(),
+ configPermission.areGidsPerUser());
+ }
+ mRegistry.addPermissionLocked(permission);
+ } else {
+ mRegistry.addPermissionTreeLocked(permission);
+ }
+ }
+ }
+ }
+ }
+
+ private void writeLegacyPermissions(
+ @NonNull LegacyPermissionSettings legacyPermissionSettings) {
+ for (int writePermissionOrPermissionTree = 0; writePermissionOrPermissionTree < 2;
+ writePermissionOrPermissionTree++) {
+ final List<LegacyPermission> legacyPermissions = new ArrayList<>();
+ synchronized (mLock) {
+ final Collection<Permission> permissions = writePermissionOrPermissionTree == 0
+ ? mRegistry.getPermissionsLocked() : mRegistry.getPermissionTreesLocked();
+ for (final Permission permission : permissions) {
+ // We don't need to provide UID and GIDs, which are only retrieved when dumping.
+ final LegacyPermission legacyPermission = new LegacyPermission(
+ permission.getPermissionInfo(), permission.getType(), 0,
+ EmptyArray.INT);
+ legacyPermissions.add(legacyPermission);
+ }
+ }
+ if (writePermissionOrPermissionTree == 0) {
+ legacyPermissionSettings.replacePermissions(legacyPermissions);
+ } else {
+ legacyPermissionSettings.replacePermissionTrees(legacyPermissions);
+ }
+ }
+ }
+
+ private void transferPermissions(@NonNull String oldPackageName,
+ @NonNull String newPackageName) {
+ synchronized (mLock) {
+ mRegistry.transferPermissionsLocked(oldPackageName, newPackageName);
+ }
+ }
+
+ private boolean canPropagatePermissionToInstantApp(@NonNull String permissionName) {
+ synchronized (mLock) {
+ final Permission bp = mRegistry.getPermission(permissionName);
+ return bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant();
+ }
+ }
+
+ @NonNull
+ private List<LegacyPermission> getLegacyPermissions() {
+ synchronized (mLock) {
+ final List<LegacyPermission> legacyPermissions = new ArrayList<>();
+ for (final Permission permission : mRegistry.getPermissionsLocked()) {
+ final LegacyPermission legacyPermission = new LegacyPermission(
+ permission.getPermissionInfo(), permission.getType(), permission.getUid(),
+ permission.getRawGids());
+ legacyPermissions.add(legacyPermission);
+ }
+ return legacyPermissions;
+ }
+ }
+
+ @NonNull
+ private Map<String, Set<String>> getAllAppOpPermissionPackages() {
+ synchronized (mLock) {
+ final ArrayMap<String, ArraySet<String>> appOpPermissionPackages =
+ mRegistry.getAllAppOpPermissionPackagesLocked();
+ final Map<String, Set<String>> deepClone = new ArrayMap<>();
+ final int appOpPermissionPackagesSize = appOpPermissionPackages.size();
+ for (int i = 0; i < appOpPermissionPackagesSize; i++) {
+ final String appOpPermission = appOpPermissionPackages.keyAt(i);
+ final ArraySet<String> packageNames = appOpPermissionPackages.valueAt(i);
+ deepClone.put(appOpPermission, new ArraySet<>(packageNames));
+ }
+ return deepClone;
+ }
+ }
+
@NonNull
private LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) {
final LegacyPermissionState legacyState = new LegacyPermissionState();
@@ -4772,9 +4867,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final PermissionState permissionState = permissionStates.get(i);
final LegacyPermissionState.PermissionState legacyPermissionState =
- new LegacyPermissionState.PermissionState(
- permissionState.getPermission(), permissionState.isGranted(),
- permissionState.getFlags());
+ new LegacyPermissionState.PermissionState(permissionState.getName(),
+ permissionState.isGranted(), permissionState.getFlags());
if (permissionState.isRuntime()) {
legacyState.putRuntimePermissionState(legacyPermissionState, userId);
} else if (userId == UserHandle.USER_SYSTEM) {
@@ -4842,12 +4936,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
PermissionManagerService.this.removeAllPermissions(pkg, chatty);
}
@Override
- public void readStateFromPackageSettingsTEMP() {
- PermissionManagerService.this.readStateFromPackageSettings();
+ public void readLegacyPermissionStateTEMP() {
+ PermissionManagerService.this.readLegacyPermissionState();
}
@Override
- public void writeStateToPackageSettingsTEMP() {
- PermissionManagerService.this.writeStateToPackageSettings();
+ public void writeLegacyPermissionStateTEMP() {
+ PermissionManagerService.this.writeLegacyPermissionState();
}
@Override
public void onUserRemoved(@UserIdInt int userId) {
@@ -4950,17 +5044,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public void enforceGrantRevokeRuntimePermissionPermissions(String message) {
- PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message);
- }
- @Override
- public PermissionSettings getPermissionSettings() {
- return mSettings;
- }
- @Override
- public BasePermission getPermissionTEMP(String permName) {
+ public Permission getPermissionTEMP(String permName) {
synchronized (PermissionManagerService.this.mLock) {
- return mSettings.getPermissionLocked(permName);
+ return mRegistry.getPermissionLocked(permName);
}
}
@@ -4970,13 +5056,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>();
synchronized (mLock) {
- int numTotalPermissions = mSettings.mPermissions.size();
-
- for (int i = 0; i < numTotalPermissions; i++) {
- BasePermission bp = mSettings.mPermissions.valueAt(i);
-
- if (bp.getProtection() == protection) {
- matchingPermissions.add(bp.generatePermissionInfo(0));
+ for (final Permission permission : mRegistry.getPermissionsLocked()) {
+ if (permission.getProtection() == protection) {
+ matchingPermissions.add(permission.generatePermissionInfo(0));
}
}
}
@@ -4990,13 +5072,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>();
synchronized (mLock) {
- int numTotalPermissions = mSettings.mPermissions.size();
-
- for (int i = 0; i < numTotalPermissions; i++) {
- BasePermission bp = mSettings.mPermissions.valueAt(i);
-
- if ((bp.getProtectionFlags() & protectionFlags) == protectionFlags) {
- matchingPermissions.add(bp.generatePermissionInfo(0));
+ for (final Permission permission : mRegistry.getPermissionsLocked()) {
+ if ((permission.getProtectionFlags() & protectionFlags) == protectionFlags) {
+ matchingPermissions.add(permission.generatePermissionInfo(0));
}
}
}
@@ -5187,25 +5265,62 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions) {
+ public void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissionNames) {
synchronized (mLock) {
- Iterator<String> iterator = permissions.iterator();
+ Iterator<String> iterator = permissionNames.iterator();
while (iterator.hasNext()) {
- String permission = iterator.next();
- BasePermission basePermission = mSettings.mPermissions.get(permission);
- if (basePermission == null || !basePermission.isHardOrSoftRestricted()) {
+ final String permissionName = iterator.next();
+ final Permission permission = mRegistry.getPermissionLocked(permissionName);
+ if (permission == null || !permission.isHardOrSoftRestricted()) {
iterator.remove();
}
}
}
}
+ @Override
+ public void readLegacyPermissionsTEMP(
+ @NonNull LegacyPermissionSettings legacyPermissionSettings) {
+ PermissionManagerService.this.readLegacyPermissions(legacyPermissionSettings);
+ }
+
+ @Override
+ public void writeLegacyPermissionsTEMP(
+ @NonNull LegacyPermissionSettings legacyPermissionSettings) {
+ PermissionManagerService.this.writeLegacyPermissions(legacyPermissionSettings);
+ }
+
+ @Override
+ public void transferPermissions(@NonNull String oldPackageName,
+ @NonNull String newPackageName) {
+ PermissionManagerService.this.transferPermissions(oldPackageName, newPackageName);
+ }
+
+ @Override
+ public boolean canPropagatePermissionToInstantApp(@NonNull String permissionName) {
+ return PermissionManagerService.this.canPropagatePermissionToInstantApp(permissionName);
+ }
+
+ @NonNull
+ @Override
+ public List<LegacyPermission> getLegacyPermissions() {
+ return PermissionManagerService.this.getLegacyPermissions();
+ }
+
@NonNull
+ @Override
+ public Map<String, Set<String>> getAllAppOpPermissionPackages() {
+ return PermissionManagerService.this.getAllAppOpPermissionPackages();
+ }
+
+ @NonNull
+ @Override
public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) {
return PermissionManagerService.this.getLegacyPermissionState(appId);
}
@NonNull
+ @Override
public int[] getGidsForUid(int uid) {
return PermissionManagerService.this.getGidsForUid(uid);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 20e9c5dcb521..df81bac99a21 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -280,21 +280,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
/**
- * Read permission state from package settings.
+ * Read legacy permission state from package settings.
*
* TODO(zhanghai): This is a temporary method because we should not expose
* {@code PackageSetting} which is a implementation detail that permission should not know.
* Instead, it should retrieve the legacy state via a defined API.
*/
- public abstract void readStateFromPackageSettingsTEMP();
+ public abstract void readLegacyPermissionStateTEMP();
/**
- * Write permission state to package settings.
+ * Write legacy permission state to package settings.
*
* TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence
* for permission.
*/
- public abstract void writeStateToPackageSettingsTEMP();
+ public abstract void writeLegacyPermissionStateTEMP();
/**
* Notify that a user has been removed and its permission state should be removed as well.
@@ -367,16 +367,14 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
public abstract void enforceCrossUserPermission(int callingUid, int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, @NonNull String message);
- public abstract void enforceGrantRevokeRuntimePermissionPermissions(@NonNull String message);
-
- public abstract @NonNull PermissionSettings getPermissionSettings();
/** Grants default browser permissions to the given package */
public abstract void grantDefaultPermissionsToDefaultBrowser(
@NonNull String packageName, @UserIdInt int userId);
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
- public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
+ @Nullable
+ public abstract Permission getPermissionTEMP(@NonNull String permName);
/** Get all permissions that have a certain protection */
public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtection(
@@ -536,5 +534,39 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* Removes invalid permissions which are not {@link PermissionInfo#FLAG_HARD_RESTRICTED} or
* {@link PermissionInfo#FLAG_SOFT_RESTRICTED} from the input.
*/
- public abstract void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions);
+ public abstract void retainHardAndSoftRestrictedPermissions(
+ @NonNull List<String> permissionNames);
+
+ /**
+ * Read legacy permissions from legacy permission settings.
+ *
+ * TODO(zhanghai): This is a temporary method because we should not expose
+ * {@code LegacyPermissionSettings} which is a implementation detail that permission should not
+ * know. Instead, it should retrieve the legacy permissions via a defined API.
+ */
+ public abstract void readLegacyPermissionsTEMP(
+ @NonNull LegacyPermissionSettings legacyPermissionSettings);
+
+ /**
+ * Write legacy permissions to legacy permission settings.
+ *
+ * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence
+ * for permission.
+ */
+ public abstract void writeLegacyPermissionsTEMP(
+ @NonNull LegacyPermissionSettings legacyPermissionSettings);
+
+ /**
+ * Transfers ownership of permissions from one package to another.
+ */
+ public abstract void transferPermissions(@NonNull String oldPackageName,
+ @NonNull String newPackageName);
+
+ /**
+ * Check whether a permission can be propagated to instant app.
+ *
+ * @param permissionName the name of the permission
+ * @return whether the permission can be propagated
+ */
+ public abstract boolean canPropagatePermissionToInstantApp(@NonNull String permissionName);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
new file mode 100644
index 000000000000..36719203e4b5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Collection;
+
+/**
+ * Permission registry for permissions, permission trees, permission groups and related things.
+ */
+public class PermissionRegistry {
+ /**
+ * All of the permissions known to the system. The mapping is from permission
+ * name to permission object.
+ */
+ @GuardedBy("mLock")
+ private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>();
+
+ /**
+ * All permission trees known to the system. The mapping is from permission tree
+ * name to permission object.
+ */
+ @GuardedBy("mLock")
+ private final ArrayMap<String, Permission> mPermissionTrees = new ArrayMap<>();
+
+ /**
+ * All permisson groups know to the system. The mapping is from permission group
+ * name to permission group object.
+ */
+ @GuardedBy("mLock")
+ private final ArrayMap<String, ParsedPermissionGroup> mPermissionGroups = new ArrayMap<>();
+
+ /**
+ * Set of packages that request a particular app op. The mapping is from permission
+ * name to package names.
+ */
+ @GuardedBy("mLock")
+ private final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
+
+ @NonNull
+ private final Object mLock;
+
+ public PermissionRegistry(@NonNull Object lock) {
+ mLock = lock;
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ public Collection<Permission> getPermissionsLocked() {
+ return mPermissions.values();
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ public Permission getPermissionLocked(@NonNull String permName) {
+ return mPermissions.get(permName);
+ }
+
+ @Nullable
+ public Permission getPermission(@NonNull String permissionName) {
+ synchronized (mLock) {
+ return getPermissionLocked(permissionName);
+ }
+ }
+
+ @GuardedBy("mLock")
+ public void addPermissionLocked(@NonNull Permission permission) {
+ mPermissions.put(permission.getName(), permission);
+ }
+
+ @GuardedBy("mLock")
+ public void removePermissionLocked(@NonNull String permissionName) {
+ mPermissions.remove(permissionName);
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ public Collection<Permission> getPermissionTreesLocked() {
+ return mPermissionTrees.values();
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ public Permission getPermissionTreeLocked(@NonNull String permissionTreeName) {
+ return mPermissionTrees.get(permissionTreeName);
+ }
+
+ @GuardedBy("mLock")
+ public void addPermissionTreeLocked(@NonNull Permission permissionTree) {
+ mPermissionTrees.put(permissionTree.getName(), permissionTree);
+ }
+
+ /**
+ * Transfers ownership of permissions from one package to another.
+ */
+ public void transferPermissionsLocked(@NonNull String oldPackageName,
+ @NonNull String newPackageName) {
+ for (int i = 0; i < 2; i++) {
+ ArrayMap<String, Permission> permissions = i == 0 ? mPermissionTrees : mPermissions;
+ for (final Permission permission : permissions.values()) {
+ permission.transfer(oldPackageName, newPackageName);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ public Collection<ParsedPermissionGroup> getPermissionGroupsLocked() {
+ return mPermissionGroups.values();
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ public ParsedPermissionGroup getPermissionGroupLocked(@NonNull String permissionGroupName) {
+ return mPermissionGroups.get(permissionGroupName);
+ }
+
+ @GuardedBy("mLock")
+ public void addPermissionGroupLocked(@NonNull ParsedPermissionGroup permissionGroup) {
+ mPermissionGroups.put(permissionGroup.getName(), permissionGroup);
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ public ArrayMap<String, ArraySet<String>> getAllAppOpPermissionPackagesLocked() {
+ return mAppOpPermissionPackages;
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ public ArraySet<String> getAppOpPermissionPackagesLocked(@NonNull String permissionName) {
+ return mAppOpPermissionPackages.get(permissionName);
+ }
+
+ @GuardedBy("mLock")
+ public void addAppOpPermissionPackageLocked(@NonNull String permissionName,
+ @NonNull String packageName) {
+ ArraySet<String> packageNames = mAppOpPermissionPackages.get(permissionName);
+ if (packageNames == null) {
+ packageNames = new ArraySet<>();
+ mAppOpPermissionPackages.put(permissionName, packageNames);
+ }
+ packageNames.add(packageName);
+ }
+
+ @GuardedBy("mLock")
+ public void removeAppOpPermissionPackageLocked(@NonNull String permissionName,
+ @NonNull String packageName) {
+ final ArraySet<String> packageNames = mAppOpPermissionPackages.get(permissionName);
+ if (packageNames == null) {
+ return;
+ }
+ final boolean removed = packageNames.remove(packageName);
+ if (removed && packageNames.isEmpty()) {
+ mAppOpPermissionPackages.remove(permissionName);
+ }
+ }
+
+ /**
+ * Returns the permission tree for the given permission.
+ * @throws SecurityException If the calling UID is not allowed to add permissions to the
+ * found permission tree.
+ */
+ @NonNull
+ public Permission enforcePermissionTree(@NonNull String permissionName, int callingUid) {
+ synchronized (mLock) {
+ return Permission.enforcePermissionTree(mPermissionTrees.values(), permissionName,
+ callingUid);
+ }
+ }
+
+ public boolean isPermissionInstant(@NonNull String permissionName) {
+ synchronized (mLock) {
+ final Permission permission = mPermissions.get(permissionName);
+ return permission != null && permission.isInstant();
+ }
+ }
+
+ boolean isPermissionAppOp(@NonNull String permissionName) {
+ synchronized (mLock) {
+ final Permission permission = mPermissions.get(permissionName);
+ return permission != null && permission.isAppOp();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
deleted file mode 100644
index eea8ac737b86..000000000000
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.permission;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.XmlUtils;
-import com.android.server.pm.DumpState;
-import com.android.server.pm.PackageManagerService;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Collection;
-
-/**
- * Permissions and other related data. This class is not meant for
- * direct access outside of the permission package with the sole exception
- * of package settings. Instead, it should be reference either from the
- * permission manager or package settings.
- */
-public class PermissionSettings {
-
- /**
- * All of the permissions known to the system. The mapping is from permission
- * name to permission object.
- */
- @GuardedBy("mLock")
- final ArrayMap<String, BasePermission> mPermissions =
- new ArrayMap<String, BasePermission>();
-
- /**
- * All permission trees known to the system. The mapping is from permission tree
- * name to permission object.
- */
- @GuardedBy("mLock")
- final ArrayMap<String, BasePermission> mPermissionTrees =
- new ArrayMap<String, BasePermission>();
-
- /**
- * All permisson groups know to the system. The mapping is from permission group
- * name to permission group object.
- */
- @GuardedBy("mLock")
- final ArrayMap<String, ParsedPermissionGroup> mPermissionGroups =
- new ArrayMap<>();
-
- /**
- * Set of packages that request a particular app op. The mapping is from permission
- * name to package names.
- */
- @GuardedBy("mLock")
- final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
-
- private final Object mLock;
-
- PermissionSettings(@NonNull Object lock) {
- mLock = lock;
- }
-
- public @Nullable BasePermission getPermission(@NonNull String permName) {
- synchronized (mLock) {
- return getPermissionLocked(permName);
- }
- }
-
- public void addAppOpPackage(String permName, String packageName) {
- synchronized (mLock) {
- ArraySet<String> pkgs = mAppOpPermissionPackages.get(permName);
- if (pkgs == null) {
- pkgs = new ArraySet<>();
- mAppOpPermissionPackages.put(permName, pkgs);
- }
- pkgs.add(packageName);
- }
- }
-
- /**
- * Transfers ownership of permissions from one package to another.
- */
- public void transferPermissions(String origPackageName, String newPackageName) {
- synchronized (mLock) {
- for (int i=0; i<2; i++) {
- ArrayMap<String, BasePermission> permissions =
- i == 0 ? mPermissionTrees : mPermissions;
- for (BasePermission bp : permissions.values()) {
- bp.transfer(origPackageName, newPackageName);
- }
- }
- }
- }
-
- public boolean canPropagatePermissionToInstantApp(String permName) {
- synchronized (mLock) {
- final BasePermission bp = mPermissions.get(permName);
- return (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant());
- }
- }
-
- public void readPermissions(XmlPullParser parser) throws IOException, XmlPullParserException {
- synchronized (mLock) {
- readPermissions(mPermissions, parser);
- }
- }
-
- public void readPermissionTrees(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- synchronized (mLock) {
- readPermissions(mPermissionTrees, parser);
- }
- }
-
- public void writePermissions(XmlSerializer serializer) throws IOException {
- synchronized (mLock) {
- for (BasePermission bp : mPermissions.values()) {
- bp.writeLPr(serializer);
- }
- }
- }
-
- public void writePermissionTrees(XmlSerializer serializer) throws IOException {
- synchronized (mLock) {
- for (BasePermission bp : mPermissionTrees.values()) {
- bp.writeLPr(serializer);
- }
- }
- }
-
- public static void readPermissions(ArrayMap<String, BasePermission> out, XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- if (!BasePermission.readLPw(out, parser)) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Unknown element reading permissions: " + parser.getName() + " at "
- + parser.getPositionDescription());
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- public void dumpPermissions(PrintWriter pw, String packageName,
- ArraySet<String> permissionNames, boolean externalStorageEnforced,
- DumpState dumpState) {
- synchronized (mLock) {
- boolean printedSomething = false;
- for (BasePermission bp : mPermissions.values()) {
- printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
- externalStorageEnforced, printedSomething, dumpState);
- }
- if (packageName == null && permissionNames == null) {
- for (int iperm = 0; iperm<mAppOpPermissionPackages.size(); iperm++) {
- if (iperm == 0) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("AppOp Permissions:");
- }
- pw.print(" AppOp Permission ");
- pw.print(mAppOpPermissionPackages.keyAt(iperm));
- pw.println(":");
- ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
- for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
- pw.print(" "); pw.println(pkgs.valueAt(ipkg));
- }
- }
- }
- }
- }
-
- @GuardedBy("mLock")
- @Nullable BasePermission getPermissionLocked(@NonNull String permName) {
- return mPermissions.get(permName);
- }
-
- @GuardedBy("mLock")
- @Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) {
- return mPermissionTrees.get(permName);
- }
-
- @GuardedBy("mLock")
- void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
- mPermissions.put(permName, permission);
- }
-
- @GuardedBy("mLock")
- void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) {
- mPermissionTrees.put(permName, permission);
- }
-
- @GuardedBy("mLock")
- void removePermissionLocked(@NonNull String permName) {
- mPermissions.remove(permName);
- }
-
- @GuardedBy("mLock")
- void removePermissionTreeLocked(@NonNull String permName) {
- mPermissionTrees.remove(permName);
- }
-
- @GuardedBy("mLock")
- @NonNull Collection<BasePermission> getAllPermissionsLocked() {
- return mPermissions.values();
- }
-
- @GuardedBy("mLock")
- @NonNull Collection<BasePermission> getAllPermissionTreesLocked() {
- return mPermissionTrees.values();
- }
-
- /**
- * Returns the permission tree for the given permission.
- * @throws SecurityException If the calling UID is not allowed to add permissions to the
- * found permission tree.
- */
- @Nullable BasePermission enforcePermissionTree(@NonNull String permName, int callingUid) {
- synchronized (mLock) {
- return BasePermission.enforcePermissionTree(
- mPermissionTrees.values(), permName, callingUid);
- }
- }
-
- /**
- * Check whether a permission is runtime.
- *
- * @see BasePermission#isRuntime()
- */
- public boolean isPermissionRuntime(@NonNull String permName) {
- synchronized (mLock) {
- final BasePermission bp = mPermissions.get(permName);
- return (bp != null && bp.isRuntime());
- }
- }
-
- public boolean isPermissionInstant(String permName) {
- synchronized (mLock) {
- final BasePermission bp = mPermissions.get(permName);
- return (bp != null && bp.isInstant());
- }
- }
-
- boolean isPermissionAppOp(String permName) {
- synchronized (mLock) {
- final BasePermission bp = mPermissions.get(permName);
- return (bp != null && bp.isAppOp());
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java
index 59b204f7dfff..12f29d091134 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionState.java
@@ -27,7 +27,7 @@ import com.android.internal.annotations.GuardedBy;
public final class PermissionState {
@NonNull
- private final BasePermission mPermission;
+ private final Permission mPermission;
private final Object mLock = new Object();
@@ -40,7 +40,7 @@ public final class PermissionState {
@GuardedBy("mLock")
private int mFlags;
- public PermissionState(@NonNull BasePermission permission) {
+ public PermissionState(@NonNull Permission permission) {
mPermission = permission;
}
@@ -52,7 +52,7 @@ public final class PermissionState {
}
@NonNull
- public BasePermission getPermission() {
+ public Permission getPermission() {
return mPermission;
}
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
index 9727a5452440..04d82e872ae6 100644
--- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -125,7 +125,7 @@ public final class UidPermissionState {
}
@NonNull
- private PermissionState getOrCreatePermissionState(@NonNull BasePermission permission) {
+ private PermissionState getOrCreatePermissionState(@NonNull Permission permission) {
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
}
@@ -158,7 +158,7 @@ public final class UidPermissionState {
* @param granted whether the permission is granted
* @param flags the permission flags
*/
- public void putPermissionState(@NonNull BasePermission permission, boolean granted, int flags) {
+ public void putPermissionState(@NonNull Permission permission, boolean granted, int flags) {
final String name = permission.getName();
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
@@ -230,7 +230,7 @@ public final class UidPermissionState {
* @param permission the permission to grant
* @return whether the permission grant state changed
*/
- public boolean grantPermission(@NonNull BasePermission permission) {
+ public boolean grantPermission(@NonNull Permission permission) {
final PermissionState permissionState = getOrCreatePermissionState(permission);
return permissionState.grant();
}
@@ -241,7 +241,7 @@ public final class UidPermissionState {
* @param permission the permission to revoke
* @return whether the permission grant state changed
*/
- public boolean revokePermission(@NonNull BasePermission permission) {
+ public boolean revokePermission(@NonNull Permission permission) {
final String name = permission.getName();
final PermissionState permissionState = getPermissionState(name);
if (permissionState == null) {
@@ -276,7 +276,7 @@ public final class UidPermissionState {
* @param flagValues the new values for the masked flags
* @return whether the permission flags changed
*/
- public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask,
+ public boolean updatePermissionFlags(@NonNull Permission permission, int flagMask,
int flagValues) {
if (flagMask == 0) {
return false;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0983e10d2965..8b677a99b22a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1468,10 +1468,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
if (mHasFeatureLeanback) {
isSetupComplete &= isTvUserSetupComplete();
+ } else if (mHasFeatureAuto) {
+ isSetupComplete &= isAutoUserSetupComplete();
}
return isSetupComplete;
}
+ private boolean isAutoUserSetupComplete() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ "android.car.SETUP_WIZARD_IN_PROGRESS", 0, UserHandle.USER_CURRENT) == 0;
+ }
+
private boolean isTvUserSetupComplete() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 61b672d6777c..a02701f8ad00 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4172,13 +4172,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- @Override
- public int getMaxNumPictureInPictureActions(IBinder token) {
- // Currently, this is a static constant, but later, we may change this to be dependent on
- // the context of the activity
- return 3;
- }
-
/**
* Checks the state of the system and the activity associated with the given {@param token} to
* verify that picture-in-picture is supported for that activity.
@@ -4216,7 +4209,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
// Truncate the number of actions if necessary
- params.truncateActions(getMaxNumPictureInPictureActions(token));
+ params.truncateActions(ActivityTaskManager.getMaxNumPictureInPictureActions(mContext));
return r;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 5e1a26b91863..ce20dbdb2997 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -397,6 +397,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
void setOrganizer(IDisplayAreaOrganizer organizer) {
+ setOrganizer(organizer, false /* skipDisplayAreaAppeared */);
+ }
+
+ void setOrganizer(IDisplayAreaOrganizer organizer, boolean skipDisplayAreaAppeared) {
if (mOrganizer == organizer) return;
IDisplayAreaOrganizer lastOrganizer = mOrganizer;
// Update the new display area organizer before calling sendDisplayAreaVanished since it
@@ -404,7 +408,9 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
// about it.
mOrganizer = organizer;
sendDisplayAreaVanished(lastOrganizer);
- sendDisplayAreaAppeared();
+ if (!skipDisplayAreaAppeared) {
+ sendDisplayAreaAppeared();
+ }
}
void sendDisplayAreaAppeared() {
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 667f3dc9d8d3..43b9a218d072 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -18,16 +18,20 @@ package com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
+import android.window.DisplayAreaAppearedInfo;
import android.window.IDisplayAreaOrganizer;
import android.window.IDisplayAreaOrganizerController;
import com.android.internal.protolog.common.ProtoLog;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub {
private static final String TAG = "DisplayAreaOrganizerController";
@@ -64,7 +68,8 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
}
@Override
- public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) {
+ public ParceledListSlice<DisplayAreaAppearedInfo> registerOrganizer(
+ IDisplayAreaOrganizer organizer, int feature) {
enforceTaskPermission("registerOrganizer()");
final long uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
@@ -83,12 +88,18 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
} catch (RemoteException e) {
// Oh well...
}
+
+ final List<DisplayAreaAppearedInfo> displayAreaInfos = new ArrayList<>();
mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
if (da.mFeatureId != feature) return;
- da.setOrganizer(organizer);
+ da.setOrganizer(organizer, true /* skipDisplayAreaAppeared */);
+ displayAreaInfos.add(new DisplayAreaAppearedInfo(da.getDisplayAreaInfo(),
+ new SurfaceControl(da.getSurfaceControl(),
+ "DisplayAreaOrganizerController.registerOrganizer")));
});
mOrganizersByFeatureIds.put(feature, organizer);
+ return new ParceledListSlice<>(displayAreaInfos);
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 7ed22a1f7777..1a27b1bc4a6e 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -181,9 +181,25 @@ class ScreenRotationAnimation {
mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
// Check whether the current screen contains any secure content.
- final boolean isSecure = displayContent.hasSecureWindowOnScreen();
+ boolean isSecure = displayContent.hasSecureWindowOnScreen();
+ final int displayId = displayContent.getDisplayId();
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
+
try {
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ mService.mDisplayManagerInternal.systemScreenshot(displayId);
+ if (screenshotBuffer == null) {
+ Slog.w(TAG, "Unable to take screenshot of display " + displayId);
+ return;
+ }
+
+ // If the screenshot contains secure layers, we have to make sure the
+ // screenshot surface we display it in also has FLAG_SECURE so that
+ // the user can not screenshot secure layers via the screenshot surface.
+ if (screenshotBuffer.containsSecureLayers()) {
+ isSecure = true;
+ }
+
mBackColorSurface = displayContent.makeChildSurface(null)
.setName("BackColorSurface")
.setColorLayer()
@@ -203,51 +219,39 @@ class ScreenRotationAnimation {
.setCallsite("ScreenRotationAnimation")
.build();
- // Capture a screenshot into the surface we just created.
- final int displayId = displayContent.getDisplayId();
final Surface surface = mService.mSurfaceFactory.get();
- // In case display bounds change, screenshot buffer and surface may mismatch so set a
- // scaling mode.
+ // In case display bounds change, screenshot buffer and surface may mismatch so
+ // set a scaling mode.
surface.copyFrom(mScreenshotLayer);
surface.setScalingMode(Surface.SCALING_MODE_SCALE_TO_WINDOW);
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- mService.mDisplayManagerInternal.systemScreenshot(displayId);
- if (screenshotBuffer != null) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
- "ScreenRotationAnimation#getMedianBorderLuma");
- mStartLuma = RotationAnimationUtils.getMedianBorderLuma(
- screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace());
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- try {
- surface.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
- screenshotBuffer.getColorSpace());
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
- }
- // If the screenshot contains secure layers, we have to make sure the
- // screenshot surface we display it in also has FLAG_SECURE so that
- // the user can not screenshot secure layers via the screenshot surface.
- if (screenshotBuffer.containsSecureLayers()) {
- t.setSecure(mScreenshotLayer, true);
- }
- t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
- t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
- t.setLayer(mBackColorSurface, -1);
- t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
- t.setAlpha(mBackColorSurface, 1);
- t.show(mScreenshotLayer);
- t.show(mBackColorSurface);
- } else {
- Slog.w(TAG, "Unable to take screenshot of display " + displayId);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+ "ScreenRotationAnimation#getMedianBorderLuma");
+ mStartLuma = RotationAnimationUtils.getMedianBorderLuma(
+ screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace());
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ try {
+ surface.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
+ screenshotBuffer.getColorSpace());
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
}
+
+ t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
+ t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
+ t.setLayer(mBackColorSurface, -1);
+ t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
+ t.setAlpha(mBackColorSurface, 1);
+ t.show(mScreenshotLayer);
+ t.show(mBackColorSurface);
surface.destroy();
+
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
- " FREEZE %s: CREATE", mScreenshotLayer);
+ " FREEZE %s: CREATE", mScreenshotLayer);
setRotation(t, realOriginalRotation);
t.apply();
}
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index d9365c5d0666..6f434e05c800 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -137,6 +137,7 @@ class SurfaceFreezer {
new SurfaceControl.LayerCaptureArgs.Builder(target)
.setSourceCrop(cropBounds)
.setCaptureSecureLayers(true)
+ .setAllowProtected(true)
.build();
return SurfaceControl.captureLayers(captureArgs);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3f15ff8ebd25..ab2f42426153 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2176,6 +2176,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final DisplayContent dc = getDisplayContent();
if (isInputMethodTarget()) {
+ // Make sure to set mInputMethodTarget as null when the removed window is the IME
+ // target, in case computeImeTarget may use the outdated target.
+ dc.mInputMethodTarget = null;
dc.computeImeTarget(true /* updateImeTarget */);
}
if (dc.mInputMethodInputTarget == this) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 871364569839..1e959e54555f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -631,25 +631,25 @@ class WindowStateAnimator {
}
private boolean shouldConsumeMainWindowSizeTransaction() {
- // If we use BLASTSync we always consume the transaction when finishing
- // the sync.
- if (mService.useBLASTSync()) {
- return false;
- }
- // We only consume the transaction when the client is calling relayout
- // because this is the only time we know the frameNumber will be valid
- // due to the client renderer being paused. Put otherwise, only when
- // mInRelayout is true can we guarantee the next frame will contain
- // the most recent configuration.
- if (!mWin.mInRelayout) return false;
- // Since we can only do this for one window, we focus on the main application window
- if (mAttrType != TYPE_BASE_APPLICATION) return false;
- final Task task = mWin.getTask();
- if (task == null) return false;
- if (task.getMainWindowSizeChangeTransaction() == null) return false;
- // Likewise we only focus on the task root, since we can only use one window
- if (!mWin.mActivityRecord.isRootOfTask()) return false;
- return true;
+ // If we use BLASTSync we always consume the transaction when finishing
+ // the sync.
+ if (mService.useBLASTSync() && mWin.useBLASTSync()) {
+ return false;
+ }
+ // We only consume the transaction when the client is calling relayout
+ // because this is the only time we know the frameNumber will be valid
+ // due to the client renderer being paused. Put otherwise, only when
+ // mInRelayout is true can we guarantee the next frame will contain
+ // the most recent configuration.
+ if (!mWin.mInRelayout) return false;
+ // Since we can only do this for one window, we focus on the main application window
+ if (mAttrType != TYPE_BASE_APPLICATION) return false;
+ final Task task = mWin.getTask();
+ if (task == null) return false;
+ if (task.getMainWindowSizeChangeTransaction() == null) return false;
+ // Likewise we only focus on the task root, since we can only use one window
+ if (!mWin.mActivityRecord.isRootOfTask()) return false;
+ return true;
}
void setSurfaceBoundariesLocked(SurfaceControl.Transaction t, final boolean recoveringMemory) {
diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
index 141561839ce3..261dda7ab15d 100644
--- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
@@ -17,6 +17,7 @@
package com.android.server.wm.utils;
import static android.hardware.HardwareBuffer.RGBA_8888;
+import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT;
import android.graphics.Color;
import android.graphics.ColorSpace;
@@ -38,12 +39,21 @@ import java.util.Arrays;
public class RotationAnimationUtils {
/**
+ * @return whether the hardwareBuffer passed in is marked as protected.
+ */
+ public static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) {
+ return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT;
+ }
+
+ /**
* Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the
* luminance at the borders of the bitmap
* @return the average luminance of all the pixels at the borders of the bitmap
*/
public static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) {
- if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888) {
+ // Cannot read content from buffer with protected usage.
+ if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888
+ || hasProtectedContent(hardwareBuffer)) {
return 0;
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 9f83bafbed75..aac60b406ca8 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -27,7 +27,6 @@ cc_library_static {
"com_android_server_adb_AdbDebuggingManager.cpp",
"com_android_server_am_BatteryStatsService.cpp",
"com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
- "com_android_server_connectivity_Vpn.cpp",
"com_android_server_ConsumerIrService.cpp",
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
"com_android_server_gpu_GpuService.cpp",
@@ -62,6 +61,8 @@ cc_library_static {
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
"onload.cpp",
":lib_networkStatsFactory_native",
+ // TODO: move the file below to the connectivity APEX
+ "com_android_server_connectivity_Vpn.cpp",
],
include_dirs: [
diff --git a/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp b/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp
index 6faffb886209..e994c03a9239 100644
--- a/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp
+++ b/services/core/jni/com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp
@@ -78,12 +78,6 @@ static const JNINativeMethod method_table[] = {
reinterpret_cast<void*>(convertSurfaceToNativeHandle)},
};
-int register_android_server_FingerprintService(JNIEnv* env) {
- return jniRegisterNativeMethods(env,
- "com/android/server/biometrics/sensors/fingerprint/FingerprintService",
- method_table, NELEM(method_table));
-}
-
int register_android_server_FaceService(JNIEnv* env) {
return jniRegisterNativeMethods(env, "com/android/server/biometrics/sensors/face/FaceService",
method_table, NELEM(method_table));
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 0ffa5c39bacc..79b5fed3e448 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -62,7 +62,6 @@ int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTrac
int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
int register_android_server_AdbDebuggingManager(JNIEnv* env);
-int register_android_server_FingerprintService(JNIEnv* env);
int register_android_server_FaceService(JNIEnv* env);
int register_android_server_GpuService(JNIEnv* env);
};
@@ -120,7 +119,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
register_android_server_stats_pull_StatsPullAtomService(env);
register_android_server_AdbDebuggingManager(env);
- register_android_server_FingerprintService(env);
register_android_server_FaceService(env);
register_android_server_GpuService(env);
return JNI_VERSION_1_4;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9edd7fbbb84c..4d553e2f92aa 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3240,7 +3240,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(adminReceiver, "ComponentName is null");
- enforceShell("forceRemoveActiveAdmin");
+ Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
+ "Non-shell user attempted to call forceRemoveActiveAdmin");
mInjector.binderWithCleanCallingIdentity(() -> {
synchronized (getLockObject()) {
if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) {
@@ -3319,13 +3320,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return (admin != null) && admin.testOnlyAdmin;
}
- private void enforceShell(String method) {
- final int callingUid = mInjector.binderGetCallingUid();
- if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
- throw new SecurityException("Non-shell user attempted to call " + method);
- }
- }
-
@Override
public void removeActiveAdmin(ComponentName adminReceiver, int userHandle) {
if (!mHasFeature) {
@@ -7255,10 +7249,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throw new IllegalArgumentException("Invalid component " + admin
+ " for device owner");
}
- final boolean hasIncompatibleAccountsOrNonAdb =
- hasIncompatibleAccountsOrNonAdbNoLock(userId, admin);
+
+ final CallerIdentity caller = getCallerIdentity();
synchronized (getLockObject()) {
- enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccountsOrNonAdb);
+ enforceCanSetDeviceOwnerLocked(caller, admin, userId);
final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
if (activeAdmin == null
|| getUserData(userId).mRemovingAdmins.contains(admin)) {
@@ -7267,7 +7261,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Shutting down backup manager service permanently.
toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false);
- if (isAdb()) {
+ if (isAdb(caller)) {
// Log device owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
DevicePolicyEventLogger
@@ -7623,10 +7617,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ " not installed for userId:" + userHandle);
}
- final boolean hasIncompatibleAccountsOrNonAdb =
- hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who);
+ final CallerIdentity caller = getCallerIdentity();
synchronized (getLockObject()) {
- enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccountsOrNonAdb);
+ enforceCanSetProfileOwnerLocked(caller, who, userHandle);
final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
if (admin == null || getUserData(userHandle).mRemovingAdmins.contains(who)) {
@@ -7644,7 +7637,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
- if (isAdb()) {
+ if (isAdb(caller)) {
// Log profile owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER);
DevicePolicyEventLogger
@@ -7837,6 +7830,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
+ final CallerIdentity caller = getCallerIdentity();
if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle)
&& getManagedUserId(userHandle) == -1) {
// No managed device, user or profile, so setting provisioning state makes no sense.
@@ -7848,7 +7842,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean transitionCheckNeeded = true;
// Calling identity/permission checks.
- if (isAdb()) {
+ if (isAdb(caller)) {
// ADB shell can only move directly from un-managed to finalized as part of directly
// setting profile-owner or device-owner.
if (getUserProvisioningState(userHandle) !=
@@ -8211,8 +8205,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* - SYSTEM_UID
* - adb unless hasIncompatibleAccountsOrNonAdb is true.
*/
- private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle,
- boolean hasIncompatibleAccountsOrNonAdb) {
+ private void enforceCanSetProfileOwnerLocked(CallerIdentity caller,
+ @Nullable ComponentName owner, int userHandle) {
UserInfo info = getUserInfo(userHandle);
if (info == null) {
// User doesn't exist.
@@ -8230,9 +8224,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throw new IllegalStateException("Trying to set the profile owner, but the user "
+ "already has a device owner.");
}
- if (isAdb()) {
+ if (isAdb(caller)) {
if ((mIsWatch || hasUserSetupCompleted(userHandle))
- && hasIncompatibleAccountsOrNonAdb) {
+ && hasIncompatibleAccountsOrNonAdbNoLock(caller, userHandle, owner)) {
throw new IllegalStateException("Not allowed to set the profile owner because "
+ "there are already some accounts on the profile");
}
@@ -8271,16 +8265,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
* permission.
*/
- private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner,
- @UserIdInt int userId,
- boolean hasIncompatibleAccountsOrNonAdb) {
- if (!isAdb()) {
+ private void enforceCanSetDeviceOwnerLocked(CallerIdentity caller,
+ @Nullable ComponentName owner, @UserIdInt int userId) {
+ if (!isAdb(caller)) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
}
- final int code = checkDeviceOwnerProvisioningPreConditionLocked(
- owner, userId, isAdb(), hasIncompatibleAccountsOrNonAdb);
+ final int code = checkDeviceOwnerProvisioningPreConditionLocked(owner, userId,
+ isAdb(caller), hasIncompatibleAccountsOrNonAdbNoLock(caller, userId, owner));
if (code != CODE_OK) {
throw new IllegalStateException(computeProvisioningErrorString(code, userId));
}
@@ -8524,6 +8517,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.print("mUserControlDisabledPackages=");
pw.println(policy.mUserControlDisabledPackages);
pw.print("mAppsSuspended="); pw.println(policy.mAppsSuspended);
+ pw.print("mUserSetupComplete="); pw.println(policy.mUserSetupComplete);
pw.decreaseIndent();
}
}
@@ -11668,7 +11662,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void clearSystemUpdatePolicyFreezePeriodRecord() {
- enforceShell("clearSystemUpdatePolicyFreezePeriodRecord");
+ Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
+ "Non-shell user attempted to call clearSystemUpdatePolicyFreezePeriodRecord");
synchronized (getLockObject()) {
// Print out current record to help diagnosed CTS failures
Slog.i(LOG_TAG, "Clear freeze period record: "
@@ -12578,23 +12573,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) {
- // As the caller is the system, it must specify the component name of the profile owner
- // as a safety check.
- Objects.requireNonNull(who);
-
if (!mHasFeature) {
return;
}
+ // As the caller is the system, it must specify the component name of the profile owner
+ // as a safety check.
+ Objects.requireNonNull(who);
+ final CallerIdentity caller = getCallerIdentity();
// Only adb or system apps with the right permission can mark a profile owner on
// organization-owned device.
- if (!(isAdb() || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) {
+ if (!(isAdb(caller) || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) {
throw new SecurityException(
"Only the system can mark a profile owner of organization-owned device.");
}
- if (isAdb()) {
- if (hasIncompatibleAccountsOrNonAdbNoLock(userId, who)) {
+ if (isAdb(caller)) {
+ if (hasIncompatibleAccountsOrNonAdbNoLock(caller, userId, who)) {
throw new SecurityException(
"Can only be called from ADB if the device has no accounts.");
}
@@ -12915,7 +12910,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public long forceSecurityLogs() {
- enforceShell("forceSecurityLogs");
+ Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
+ "Non-shell user attempted to call forceSecurityLogs");
if (!mInjector.securityLogGetLoggingEnabledProperty()) {
throw new IllegalStateException("logging is not available");
}
@@ -13283,9 +13279,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*
* DO NOT CALL IT WITH THE DPMS LOCK HELD.
*/
- private boolean hasIncompatibleAccountsOrNonAdbNoLock(
+ private boolean hasIncompatibleAccountsOrNonAdbNoLock(CallerIdentity caller,
int userId, @Nullable ComponentName owner) {
- if (!isAdb()) {
+ if (!isAdb(caller)) {
return true;
}
wtfIfInLock();
@@ -13340,9 +13336,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private boolean isAdb() {
- final int callingUid = mInjector.binderGetCallingUid();
- return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ private boolean isAdb(CallerIdentity caller) {
+ return isShellUid(caller) || isRootUid(caller);
}
@Override
@@ -13404,7 +13399,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public long forceNetworkLogs() {
- enforceShell("forceNetworkLogs");
+ Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
+ "Non-shell user attempted to call forceNetworkLogs");
synchronized (getLockObject()) {
if (!isNetworkLoggingEnabledInternalLocked()) {
throw new IllegalStateException("logging is not available");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e116a353c723..03edc585b46a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1131,6 +1131,7 @@ public final class SystemServer implements Dumpable {
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
IpSecService ipSecService = null;
+ VcnManagementService vcnManagement = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
IConnectivityManager connectivity = null;
@@ -1581,6 +1582,15 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
+ t.traceBegin("StartVcnManagementService");
+ try {
+ vcnManagement = VcnManagementService.create(context);
+ ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
+ } catch (Throwable e) {
+ reportWtf("starting VCN Management Service", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartTextServicesManager");
mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
t.traceEnd();
@@ -2371,6 +2381,7 @@ public final class SystemServer implements Dumpable {
final MediaRouterService mediaRouterF = mediaRouter;
final MmsServiceBroker mmsServiceF = mmsService;
final IpSecService ipSecServiceF = ipSecService;
+ final VcnManagementService vcnManagementF = vcnManagement;
final WindowManagerService windowManagerF = wm;
final ConnectivityManager connectivityF = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -2463,6 +2474,15 @@ public final class SystemServer implements Dumpable {
reportWtf("making IpSec Service ready", e);
}
t.traceEnd();
+ t.traceBegin("MakeVcnManagementServiceReady");
+ try {
+ if (vcnManagementF != null) {
+ vcnManagementF.systemReady();
+ }
+ } catch (Throwable e) {
+ reportWtf("making VcnManagementService ready", e);
+ }
+ t.traceEnd();
t.traceBegin("MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) {
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 3c9322d2dfdc..1ccd512a9daf 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -66,5 +66,8 @@ filegroup {
":framework-annotations",
"java/android/net/util/NetworkConstants.java",
],
- visibility: ["//frameworks/base/packages/Tethering"],
+ visibility: [
+ "//frameworks/base/packages/Tethering",
+ "//packages/modules/Connectivity/Tethering"
+ ],
}
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 a5f083477863..db4aba519a6a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -17,8 +17,14 @@ package com.android.server.alarm;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
+import static android.app.AlarmManager.FLAG_STANDALONE;
+import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
+import static android.app.AlarmManager.WINDOW_EXACT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -66,7 +72,6 @@ import static org.mockito.Mockito.atLeastOnce;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.IAlarmCompleteListener;
import android.app.IAlarmListener;
@@ -85,7 +90,6 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseArray;
@@ -112,8 +116,6 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.Executor;
@@ -354,48 +356,51 @@ public class AlarmManagerServiceTest {
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
- setTestAlarm(type, triggerTime, operation, 0, AlarmManager.FLAG_STANDALONE,
- TEST_CALLING_UID);
+ setTestAlarm(type, triggerTime, operation, 0, FLAG_STANDALONE, TEST_CALLING_UID);
}
private void setRepeatingTestAlarm(int type, long firstTrigger, long interval,
PendingIntent pi) {
- setTestAlarm(type, firstTrigger, pi, interval, AlarmManager.FLAG_STANDALONE,
- TEST_CALLING_UID);
+ setTestAlarm(type, firstTrigger, pi, interval, FLAG_STANDALONE, TEST_CALLING_UID);
}
private void setIdleUntilAlarm(int type, long triggerTime, PendingIntent pi) {
- setTestAlarm(type, triggerTime, pi, 0, AlarmManager.FLAG_IDLE_UNTIL, TEST_CALLING_UID);
+ setTestAlarm(type, triggerTime, pi, 0, FLAG_IDLE_UNTIL, TEST_CALLING_UID);
}
private void setWakeFromIdle(int type, long triggerTime, PendingIntent pi) {
// Note: Only alarm clock alarms are allowed to include this flag in the actual service.
// But this is a unit test so we'll only test the flag for granularity and convenience.
- setTestAlarm(type, triggerTime, pi, 0,
- AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE, TEST_CALLING_UID);
+ setTestAlarm(type, triggerTime, pi, 0, FLAG_WAKE_FROM_IDLE | FLAG_STANDALONE,
+ TEST_CALLING_UID);
+ }
+
+ private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi,
+ boolean unrestricted) {
+ final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE;
+ setTestAlarm(type, triggerTime, pi, 0, flags, TEST_CALLING_UID);
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
int flags, int callingUid) {
- mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval, operation, null,
- "test", flags, null, null, callingUid, TEST_CALLING_PACKAGE);
+ mService.setImpl(type, triggerTime, WINDOW_EXACT, interval, operation, null, "test", flags,
+ null, null, callingUid, TEST_CALLING_PACKAGE);
}
private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
- mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, 0,
- null, listener, "test", AlarmManager.FLAG_STANDALONE, null, null,
- TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
+ FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
}
private PendingIntent getNewMockPendingIntent() {
- return getNewMockPendingIntent(TEST_CALLING_UID);
+ return getNewMockPendingIntent(TEST_CALLING_UID, TEST_CALLING_PACKAGE);
}
- private PendingIntent getNewMockPendingIntent(int mockUid) {
+ private PendingIntent getNewMockPendingIntent(int creatorUid, String creatorPackage) {
final PendingIntent mockPi = mock(PendingIntent.class, Answers.RETURNS_DEEP_STUBS);
- when(mockPi.getCreatorUid()).thenReturn(mockUid);
- when(mockPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE);
+ when(mockPi.getCreatorUid()).thenReturn(creatorUid);
+ when(mockPi.getCreatorPackage()).thenReturn(creatorPackage);
return mockPi;
}
@@ -865,7 +870,7 @@ public class AlarmManagerServiceTest {
public void alarmCountKeyedOnCallingUid() {
final int mockCreatorUid = 431412;
setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 5,
- getNewMockPendingIntent(mockCreatorUid));
+ getNewMockPendingIntent(mockCreatorUid, TEST_CALLING_PACKAGE));
assertEquals(1, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
assertEquals(-1, mService.mAlarmsPerUid.get(mockCreatorUid, -1));
}
@@ -1018,7 +1023,8 @@ public class AlarmManagerServiceTest {
for (int i = 0; i < numAlarms; i++) {
int mockUid = UserHandle.getUid(mockUserId, 1234 + i);
setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10,
- getNewMockPendingIntent(mockUid), 0, AlarmManager.FLAG_STANDALONE, mockUid);
+ getNewMockPendingIntent(mockUid, TEST_CALLING_PACKAGE), 0, FLAG_STANDALONE,
+ mockUid);
}
assertEquals(numAlarms, mService.mAlarmsPerUid.size());
mService.removeUserLocked(mockUserId);
@@ -1026,26 +1032,6 @@ public class AlarmManagerServiceTest {
}
@Test
- public void alarmCountOnRemoveFromPendingWhileIdle() {
- mService.mPendingIdleUntil = mock(Alarm.class);
- final int numAlarms = 15;
- final PendingIntent[] pis = new PendingIntent[numAlarms];
- for (int i = 0; i < numAlarms; i++) {
- pis[i] = getNewMockPendingIntent();
- setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
- }
- assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- assertEquals(numAlarms, mService.mPendingWhileIdleAlarms.size());
- final int toRemove = 8;
- for (int i = 0; i < toRemove; i++) {
- mService.removeLocked(pis[i], null);
- assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
- }
- mService.removeLocked(TEST_CALLING_UID);
- assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
- }
-
- @Test
public void alarmCountOnAlarmRemoved() {
final int numAlarms = 10;
final PendingIntent[] pis = new PendingIntent[numAlarms];
@@ -1268,6 +1254,134 @@ public class AlarmManagerServiceTest {
assertEquals(mNowElapsedTest + 12, mService.mPendingIdleUntil.getWhenElapsed());
}
+ @Test
+ public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception {
+ doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+ 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());
+ assertNotNull(mService.mPendingIdleUntil);
+
+ final long seedTrigger = mNowElapsedTest + 3;
+ final int numAlarms = 10;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ pis[i] = getNewMockPendingIntent();
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, seedTrigger + i * i, pis[i], false);
+ }
+
+ long lastAwiDispatch = -1;
+ int i = 0;
+ while (i < numAlarms) {
+ final long nextDispatch = (lastAwiDispatch >= 0) ? (lastAwiDispatch + awiDelayForTest)
+ : (seedTrigger + i * i);
+ assertEquals("Wrong allow-while-idle dispatch", nextDispatch, mTestTimer.getElapsed());
+
+ mNowElapsedTest = nextDispatch;
+ mTestTimer.expire();
+
+ while (i < numAlarms && (seedTrigger + i * i) <= nextDispatch) {
+ verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(),
+ any(Handler.class), isNull(), any());
+ i++;
+ }
+ Log.d(TAG, "Dispatched alarms upto " + i + " at " + nextDispatch);
+ lastAwiDispatch = nextDispatch;
+ }
+ }
+
+ @Test
+ public void allowWhileIdleUnrestricted() throws Exception {
+ doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+ final long awiDelayForTest = 127;
+ setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, awiDelayForTest);
+ setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, 0);
+
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1000,
+ getNewMockPendingIntent());
+ assertNotNull(mService.mPendingIdleUntil);
+
+ final long seedTrigger = mNowElapsedTest + 3;
+ for (int i = 1; i <= 5; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, seedTrigger + i * i,
+ getNewMockPendingIntent(), true);
+ }
+ for (int i = 1; i <= 5; i++) {
+ final long nextTrigger = mTestTimer.getElapsed();
+ assertEquals("Wrong trigger for alarm " + i, seedTrigger + i * i, nextTrigger);
+ mNowElapsedTest = nextTrigger;
+ mTestTimer.expire();
+ }
+ }
+
+ @Test
+ public void deviceIdleThrottling() throws Exception {
+ doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+ final long deviceIdleUntil = mNowElapsedTest + 1234;
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, deviceIdleUntil, getNewMockPendingIntent());
+
+ assertEquals(deviceIdleUntil, mTestTimer.getElapsed());
+
+ 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(deviceIdleUntil, mTestTimer.getElapsed());
+ }
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ for (int i = 0; i < numAlarms; i++) {
+ verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(),
+ any(Handler.class), isNull(), any());
+ }
+ }
+
+ @Test
+ public void dispatchOrder() throws Exception {
+ doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+ final long deviceIdleUntil = mNowElapsedTest + 1234;
+ final PendingIntent idleUntilPi = getNewMockPendingIntent();
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, deviceIdleUntil, idleUntilPi);
+
+ assertEquals(deviceIdleUntil, mTestTimer.getElapsed());
+
+ final PendingIntent pi5wakeup = getNewMockPendingIntent();
+ final PendingIntent pi4wakeupPackage = getNewMockPendingIntent();
+ final PendingIntent pi2nonWakeup = getNewMockPendingIntent(57, "test.different.package");
+
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, pi5wakeup);
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 4, pi4wakeupPackage);
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 2, pi2nonWakeup);
+
+ mNowElapsedTest = deviceIdleUntil;
+ mTestTimer.expire();
+
+ // The order of the alarms in delivery list should be:
+ // IdleUntil, all alarms of a package with any wakeup alarms, then the rest.
+ // Within a package, alarms should be ordered by requested delivery time.
+ final PendingIntent[] expectedOrder = new PendingIntent[]{
+ idleUntilPi, pi4wakeupPackage, pi5wakeup, pi2nonWakeup};
+
+ ArgumentCaptor<ArrayList<Alarm>> listCaptor = ArgumentCaptor.forClass(ArrayList.class);
+ verify(mService).deliverAlarmsLocked(listCaptor.capture(), anyLong());
+ final ArrayList<Alarm> deliveryList = listCaptor.getValue();
+
+ assertEquals(expectedOrder.length, deliveryList.size());
+ for (int i = 0; i < expectedOrder.length; i++) {
+ assertTrue("Unexpected alarm: " + deliveryList.get(i) + " at pos: " + i,
+ deliveryList.get(i).matches(expectedOrder[i], null));
+ }
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
@@ -1275,12 +1389,4 @@ public class AlarmManagerServiceTest {
}
LocalServices.removeServiceForTest(AlarmManagerInternal.class);
}
-
- private void dumpAllAlarms(String tag, ArrayList<Alarm> alarms) {
- System.out.println(tag + ": ");
- IndentingPrintWriter ipw = new IndentingPrintWriter(new PrintWriter(System.out));
- AlarmManagerService.dumpAlarmList(ipw, alarms, mNowElapsedTest,
- new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
- ipw.close();
- }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index f0490ce469dd..c4fc61a5aa6e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -20,6 +20,7 @@ import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -45,22 +46,17 @@ public class AlarmStoreTest {
mAlarmStore = new BatchingAlarmStore(null);
}
- private static Alarm createAlarm(long whenElapsed, long windowLength,
- AlarmManager.AlarmClockInfo alarmClock) {
- return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength,
- alarmClock);
+ private static Alarm createAlarm(long whenElapsed, long windowLength) {
+ return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, 0);
}
- private static Alarm createWakeupAlarm(long whenElapsed, long windowLength,
- AlarmManager.AlarmClockInfo alarmClock) {
- return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength,
- alarmClock);
+ private static Alarm createWakeupAlarm(long whenElapsed, long windowLength, int flags) {
+ return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, flags);
}
- private static Alarm createAlarm(int type, long whenElapsed, long windowLength,
- AlarmManager.AlarmClockInfo alarmClock) {
+ private static Alarm createAlarm(int type, long whenElapsed, long windowLength, int flags) {
return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class),
- null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
}
private void addAlarmsToStore(Alarm... alarms) {
@@ -71,11 +67,11 @@ public class AlarmStoreTest {
@Test
public void add() {
- final Alarm a1 = createAlarm(1, 0, null);
+ final Alarm a1 = createAlarm(1, 0);
mAlarmStore.add(a1);
assertEquals(1, mAlarmStore.size());
- final Alarm a2 = createAlarm(2, 0, null);
+ final Alarm a2 = createAlarm(2, 0);
mAlarmStore.add(a2);
assertEquals(2, mAlarmStore.size());
@@ -86,9 +82,9 @@ public class AlarmStoreTest {
@Test
public void remove() {
- final Alarm a1 = createAlarm(1, 0, null);
- final Alarm a2 = createAlarm(2, 0, null);
- final Alarm a5 = createAlarm(5, 0, null);
+ final Alarm a1 = createAlarm(1, 0);
+ final Alarm a2 = createAlarm(2, 0);
+ final Alarm a5 = createAlarm(5, 0);
addAlarmsToStore(a1, a2, a5);
ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.getWhenElapsed() < 4));
@@ -96,7 +92,7 @@ public class AlarmStoreTest {
assertEquals(1, mAlarmStore.size());
assertTrue(removed.contains(a1) && removed.contains(a2));
- final Alarm a8 = createAlarm(8, 0, null);
+ final Alarm a8 = createAlarm(8, 0);
addAlarmsToStore(a8, a2, a1);
removed = mAlarmStore.remove(unused -> false);
@@ -110,9 +106,9 @@ public class AlarmStoreTest {
@Test
public void removePendingAlarms() {
- final Alarm a1to11 = createAlarm(1, 10, null);
- final Alarm a2to5 = createAlarm(2, 3, null);
- final Alarm a6to9 = createAlarm(6, 3, null);
+ final Alarm a1to11 = createAlarm(1, 10);
+ final Alarm a2to5 = createAlarm(2, 3);
+ final Alarm a6to9 = createAlarm(6, 3);
addAlarmsToStore(a2to5, a6to9, a1to11);
final ArrayList<Alarm> pendingAt0 = mAlarmStore.removePendingAlarms(0);
@@ -134,10 +130,10 @@ public class AlarmStoreTest {
@Test
public void getNextWakeupDeliveryTime() {
- final Alarm a1to10 = createAlarm(1, 9, null);
- final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null);
- final Alarm a6wakeup = createWakeupAlarm(6, 0, null);
- final Alarm a5 = createAlarm(5, 0, null);
+ final Alarm a1to10 = createAlarm(1, 9);
+ final Alarm a3to8wakeup = createWakeupAlarm(3, 5, 0);
+ final Alarm a6wakeup = createWakeupAlarm(6, 0, 0);
+ final Alarm a5 = createAlarm(5, 0);
addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10);
// The wakeup alarms are [6] and [3, 8], hence 6 is the latest time till when we can
@@ -155,10 +151,10 @@ public class AlarmStoreTest {
@Test
public void getNextDeliveryTime() {
- final Alarm a1to10 = createAlarm(1, 9, null);
- final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null);
- final Alarm a6wakeup = createWakeupAlarm(6, 0, null);
- final Alarm a5 = createAlarm(5, 0, null);
+ final Alarm a1to10 = createAlarm(1, 9);
+ final Alarm a3to8wakeup = createWakeupAlarm(3, 5, 0);
+ final Alarm a6wakeup = createWakeupAlarm(6, 0, 0);
+ final Alarm a5 = createAlarm(5, 0);
addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10);
assertTrue(mAlarmStore.getNextDeliveryTime() <= 5);
@@ -168,10 +164,32 @@ public class AlarmStoreTest {
}
@Test
+ public void getNextWakeFromIdle() {
+ final Alarm a3 = createWakeupAlarm(3, 0, AlarmManager.FLAG_WAKE_FROM_IDLE);
+ final Alarm a5 = createWakeupAlarm(5, 0, AlarmManager.FLAG_WAKE_FROM_IDLE);
+ final Alarm a7 = createWakeupAlarm(7, 0, AlarmManager.FLAG_WAKE_FROM_IDLE);
+
+ mAlarmStore.add(a5);
+ assertEquals(a5, mAlarmStore.getNextWakeFromIdleAlarm());
+
+ mAlarmStore.add(a7);
+ assertEquals(a5, mAlarmStore.getNextWakeFromIdleAlarm());
+
+ mAlarmStore.add(a3);
+ assertEquals(a3, mAlarmStore.getNextWakeFromIdleAlarm());
+
+ mAlarmStore.remove(a -> (a == a3) || (a == a5));
+ assertEquals(a7, mAlarmStore.getNextWakeFromIdleAlarm());
+
+ mAlarmStore.remove(a -> (a == a7));
+ assertNull(mAlarmStore.getNextWakeFromIdleAlarm());
+ }
+
+ @Test
public void updateAlarmDeliveries() {
- final Alarm a5 = createAlarm(5, 0, null);
- final Alarm a8 = createAlarm(8, 0, null);
- final Alarm a10 = createAlarm(10, 0, null);
+ final Alarm a5 = createAlarm(5, 0);
+ final Alarm a8 = createAlarm(8, 0);
+ final Alarm a10 = createAlarm(10, 0);
addAlarmsToStore(a8, a10, a5);
assertEquals(5, mAlarmStore.getNextDeliveryTime());
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
index efcfae38c3d3..e80f0655e641 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
@@ -19,6 +19,7 @@ package com.android.server.alarm;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
+import static com.android.server.alarm.Alarm.NUM_POLICIES;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
@@ -36,6 +37,8 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Random;
+
@Presubmit
@RunWith(AndroidJUnit4.class)
public class AlarmTest {
@@ -49,29 +52,54 @@ public class AlarmTest {
@Test
public void initSetsOnlyRequesterPolicy() {
final Alarm a = createDefaultAlarm(4567, 2);
- assertEquals(4567, a.getPolicyElapsed(REQUESTER_POLICY_INDEX));
- assertEquals(0, a.getPolicyElapsed(APP_STANDBY_POLICY_INDEX));
+
+ for (int i = 0; i < NUM_POLICIES; i++) {
+ if (i == REQUESTER_POLICY_INDEX) {
+ assertEquals(4567, a.getPolicyElapsed(i));
+ } else {
+ assertEquals(0, a.getPolicyElapsed(i));
+ }
+ }
+ }
+
+ /**
+ * Generates a long matrix {@code A} of size {@code NxN}, with the property that the {@code i}th
+ * row will have the {@code i}th element largest in that row.
+ *
+ * In other words, {@code A[i][i]} will be the maximum of {@code A[i][j]} over all {@code j},
+ * {@code 0<=j<N}.
+ */
+ private static long[][] generatePolicyTestMatrix(int n) {
+ final long[][] data = new long[n][n];
+ final Random random = new Random(971);
+ for (int i = 0; i < n; i++) {
+ data[i][i] = 1;
+ for (int j = 0; j < n; j++) {
+ if (i != j) {
+ data[i][j] = random.nextInt(1 << 20);
+ data[i][i] += data[i][j];
+ }
+ }
+ }
+ return data;
}
@Test
public void whenElapsed() {
final Alarm a = createDefaultAlarm(0, 0);
- a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4);
- a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10);
- assertEquals(10, a.getWhenElapsed());
-
- a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 12);
- assertEquals(12, a.getWhenElapsed());
-
- a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7);
- assertEquals(10, a.getWhenElapsed());
-
- a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 2);
- assertEquals(7, a.getWhenElapsed());
-
- a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 7);
- assertEquals(7, a.getWhenElapsed());
+ final long[][] uniqueData = generatePolicyTestMatrix(NUM_POLICIES);
+ for (int i = 0; i < NUM_POLICIES; i++) {
+ for (int j = 0; j < NUM_POLICIES; j++) {
+ a.setPolicyElapsed(j, uniqueData[i][j]);
+ }
+ assertEquals(uniqueData[i][i], a.getWhenElapsed());
+ }
+
+ for (int i = 0; i < NUM_POLICIES; i++) {
+ a.setPolicyElapsed(i, 3);
+ }
+ assertEquals(3, a.getWhenElapsed());
}
@Test
@@ -85,18 +113,21 @@ public class AlarmTest {
a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 2);
assertEquals(14, a.getMaxWhenElapsed());
- a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 5);
- assertEquals(14, a.getMaxWhenElapsed());
-
- a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 16);
- assertEquals(16, a.getMaxWhenElapsed());
-
- a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 12);
- assertEquals(14, a.getMaxWhenElapsed());
+ for (int i = 0; i < NUM_POLICIES; i++) {
+ if (i == REQUESTER_POLICY_INDEX) {
+ continue;
+ }
+ a.setPolicyElapsed(i, 17);
+ // getWhenElapsed is 17, so getMaxWhenElapsed will return 17 too.
+ assertEquals(17, a.getMaxWhenElapsed());
+
+ a.setPolicyElapsed(i, 5);
+ assertEquals(14, a.getMaxWhenElapsed());
+ }
}
@Test
- public void setPolicyElapsed() {
+ public void setPolicyElapsedExact() {
final Alarm exactAlarm = createDefaultAlarm(10, 0);
assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
@@ -108,6 +139,10 @@ public class AlarmTest {
assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7));
+ }
+
+ @Test
+ public void setPolicyElapsedInexact() {
final Alarm inexactAlarm = createDefaultAlarm(10, 5);
assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index c10b7b98bb5b..6a6fb82897bb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -429,7 +429,12 @@ public class LocalDisplayAdapterTest {
public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
1000, 1000, 0);
public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
- new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f);
+ new SurfaceControl.DesiredDisplayConfigSpecs(/* defaultConfig */ 0,
+ /* allowGroupSwitching */ false,
+ /* primaryRefreshRateMin */ 60.f,
+ /* primaryRefreshRateMax */ 60.f,
+ /* appRefreshRateMin */ 60.f,
+ /* appRefreshRateMax */60.f);
private FakeDisplay(int port) {
this.address = createDisplayAddress(port);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index b98021bc2cab..79542084ac36 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -307,7 +307,9 @@ public class QuotaControllerTest {
private void trackJobs(JobStatus... jobs) {
for (JobStatus job : jobs) {
mJobStore.add(job);
- mQuotaController.maybeStartTrackingJobLocked(job, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+ }
}
}
@@ -336,15 +338,19 @@ public class QuotaControllerTest {
}
private void setDeviceConfigLong(String key, long val) {
- mQuotaController.prepareForUpdatedConstantsLocked();
mDeviceConfigPropertiesBuilder.setLong(key, val);
- mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForUpdatedConstantsLocked();
+ mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ }
}
private void setDeviceConfigInt(String key, int val) {
- mQuotaController.prepareForUpdatedConstantsLocked();
mDeviceConfigPropertiesBuilder.setInt(key, val);
- mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForUpdatedConstantsLocked();
+ mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ }
}
@Test
@@ -396,7 +402,9 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(0, "com.android.test", two);
mQuotaController.saveTimingSession(0, "com.android.test", one);
- mQuotaController.deleteObsoleteSessionsLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.deleteObsoleteSessionsLocked();
+ }
assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
}
@@ -430,15 +438,21 @@ public class QuotaControllerTest {
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
final int uid = 10001;
- mQuotaController.onAppRemovedLocked("com.android.test.remove", uid);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.onAppRemovedLocked("com.android.test.remove", uid);
+ }
assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove"));
assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay"));
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test.remove", RARE_INDEX));
- assertNotEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test.stay", RARE_INDEX));
-
- assertFalse(mQuotaController.getForegroundUids().get(uid));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test.remove", RARE_INDEX));
+ assertNotEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test.stay", RARE_INDEX));
+
+ assertFalse(mQuotaController.getForegroundUids().get(uid));
+ }
}
@Test
@@ -469,13 +483,15 @@ public class QuotaControllerTest {
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
- mQuotaController.onUserRemovedLocked(0);
- assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
- assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test"));
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
- assertNotEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(10, "com.android.test", RARE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.onUserRemovedLocked(0);
+ assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test"));
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+ assertNotEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(10, "com.android.test", RARE_INDEX));
+ }
}
@Test
@@ -504,7 +520,9 @@ public class QuotaControllerTest {
inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100;
// Invalid time is now +24 hours since there are no sessions at all for the app.
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = MINUTE_IN_MILLIS;
@@ -516,7 +534,9 @@ public class QuotaControllerTest {
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 0;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS;
@@ -527,7 +547,9 @@ public class QuotaControllerTest {
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 1;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * MINUTE_IN_MILLIS;
@@ -539,7 +561,9 @@ public class QuotaControllerTest {
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 1;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS;
@@ -551,7 +575,9 @@ public class QuotaControllerTest {
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 1;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS;
@@ -562,7 +588,9 @@ public class QuotaControllerTest {
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 2;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
@@ -576,7 +604,9 @@ public class QuotaControllerTest {
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 3;
expectedStats.inQuotaTimeElapsed = now + 11 * MINUTE_IN_MILLIS;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
@@ -590,7 +620,9 @@ public class QuotaControllerTest {
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 4;
expectedStats.inQuotaTimeElapsed = now + 5 * MINUTE_IN_MILLIS;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * HOUR_IN_MILLIS;
@@ -605,7 +637,9 @@ public class QuotaControllerTest {
// App goes under job execution time limit in ~61 minutes, but will be under job count limit
// in 65 minutes.
expectedStats.inQuotaTimeElapsed = now + 65 * MINUTE_IN_MILLIS;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS;
@@ -618,7 +652,9 @@ public class QuotaControllerTest {
expectedStats.bgJobCountInMaxPeriod = 15;
expectedStats.sessionCountInWindow = 5;
expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS + 5 * MINUTE_IN_MILLIS;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
// Make sure expirationTimeElapsed is set correctly when it's dependent on the max period.
@@ -637,7 +673,9 @@ public class QuotaControllerTest {
expectedStats.sessionCountInWindow = 5;
expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
mQuotaController.getTimingSessions(0, "com.android.test")
@@ -654,7 +692,9 @@ public class QuotaControllerTest {
expectedStats.sessionCountInWindow = 5;
expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
+ }
assertEquals(expectedStats, inputStats);
}
@@ -675,18 +715,23 @@ public class QuotaControllerTest {
for (int i = 1; i < mQcConstants.MAX_JOB_COUNT_RARE; ++i) {
JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", i);
setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(7000);
expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis();
expectedStats.executionTimeInWindowMs = expectedStats.executionTimeInMaxPeriodMs =
7000 * i;
expectedStats.bgJobCountInWindow = expectedStats.bgJobCountInMaxPeriod = i;
- mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
- assertEquals(expectedStats, inputStats);
- assertTrue(mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- RARE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
+ assertEquals(expectedStats, inputStats);
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
+ }
assertTrue("Job not ready: " + jobStatus, jobStatus.isReady());
}
@@ -704,16 +749,21 @@ public class QuotaControllerTest {
// Active timer is under quota, so out of quota due to old session.
expectedStats.inQuotaTimeElapsed =
sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS;
- mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
- assertEquals(expectedStats, inputStats);
- assertFalse(
- mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
+ assertEquals(expectedStats, inputStats);
+ assertFalse(
+ mQuotaController.isWithinQuotaLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
+ }
// Quota should be exceeded due to activity in active timer.
JobStatus jobStatus = createJobStatus("testUpdateExecutionStatsLocked_WithTimer", 0);
setStandbyBucket(RARE_INDEX, jobStatus); // 24 hour window
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(10000);
expectedStats.executionTimeInWindowMs += 10000;
@@ -724,11 +774,14 @@ public class QuotaControllerTest {
// time has passed since active timer.
expectedStats.inQuotaTimeElapsed =
sElapsedRealtimeClock.millis() + expectedStats.windowSizeMs;
- mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
- assertEquals(expectedStats, inputStats);
- assertFalse(
- mQuotaController.isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
- assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady());
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.updateExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
+ assertEquals(expectedStats, inputStats);
+ assertFalse(
+ mQuotaController.isWithinQuotaLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
+ assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady());
+ }
}
/**
@@ -758,8 +811,10 @@ public class QuotaControllerTest {
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 20;
expectedStats.sessionCountInWindow = 1;
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
+ }
// Working
expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
@@ -773,8 +828,10 @@ public class QuotaControllerTest {
expectedStats.sessionCountInWindow = 2;
expectedStats.inQuotaTimeElapsed = now + 3 * MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
+ }
// Frequent
expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
@@ -788,8 +845,11 @@ public class QuotaControllerTest {
expectedStats.sessionCountInWindow = 3;
expectedStats.inQuotaTimeElapsed = now + 6 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX));
+ }
// Rare
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
@@ -803,8 +863,10 @@ public class QuotaControllerTest {
expectedStats.sessionCountInWindow = 4;
expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+ }
}
/**
@@ -831,32 +893,41 @@ public class QuotaControllerTest {
expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
expectedStats.bgJobCountInMaxPeriod = 2;
expectedStats.sessionCountInWindow = 1;
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
+ }
// Working
expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
+ }
// Frequent
expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
expectedStats.expirationTimeElapsed = 8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX));
+ }
// Rare
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+ }
}
/**
@@ -894,105 +965,121 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
- mQuotaController.invalidateAllExecutionStatsLocked();
- assertEquals(0, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
- assertEquals(32, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
- assertEquals(128, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
- assertEquals(160, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(32, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(128, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(160, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
- mQuotaController.invalidateAllExecutionStatsLocked();
- assertEquals(0, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
- assertEquals(22, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
- assertEquals(88, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
- assertEquals(110, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(22, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(88, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(110, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
- mQuotaController.invalidateAllExecutionStatsLocked();
- assertEquals(0, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
- assertEquals(22, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
- assertEquals(88, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
- assertEquals(110, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(22, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(88, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(110, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
5 * SECOND_IN_MILLIS);
- mQuotaController.invalidateAllExecutionStatsLocked();
- assertEquals(0, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
- assertEquals(14, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
- assertEquals(56, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
- assertEquals(70, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(14, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(56, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(70, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
MINUTE_IN_MILLIS);
- mQuotaController.invalidateAllExecutionStatsLocked();
- assertEquals(0, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
- assertEquals(4, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
- assertEquals(16, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
- assertEquals(20, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(16, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
5 * MINUTE_IN_MILLIS);
- mQuotaController.invalidateAllExecutionStatsLocked();
- assertEquals(0, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
- assertEquals(2, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
- assertEquals(8, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
- assertEquals(10, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(2, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(8, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(10, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
15 * MINUTE_IN_MILLIS);
- mQuotaController.invalidateAllExecutionStatsLocked();
- assertEquals(0, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
- assertEquals(2, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
- assertEquals(8, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
- assertEquals(10, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(2, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(8, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(10, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
// QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
// between an hour and 15 minutes.
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
- mQuotaController.invalidateAllExecutionStatsLocked();
- assertEquals(0, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
- assertEquals(2, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
- assertEquals(8, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
- assertEquals(10, mQuotaController.getExecutionStatsLocked(
- 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(2, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(8, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(10, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
}
/**
@@ -1012,14 +1099,20 @@ public class QuotaControllerTest {
createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
- final ExecutionStats originalStatsActive = mQuotaController.getExecutionStatsLocked(0,
- "com.android.test", ACTIVE_INDEX);
- final ExecutionStats originalStatsWorking = mQuotaController.getExecutionStatsLocked(0,
- "com.android.test", WORKING_INDEX);
- final ExecutionStats originalStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
- "com.android.test", FREQUENT_INDEX);
- final ExecutionStats originalStatsRare = mQuotaController.getExecutionStatsLocked(0,
- "com.android.test", RARE_INDEX);
+ final ExecutionStats originalStatsActive;
+ final ExecutionStats originalStatsWorking;
+ final ExecutionStats originalStatsFrequent;
+ final ExecutionStats originalStatsRare;
+ synchronized (mQuotaController.mLock) {
+ originalStatsActive = mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX);
+ originalStatsWorking = mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX);
+ originalStatsFrequent = mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX);
+ originalStatsRare = mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX);
+ }
// Advance clock so that the working stats shouldn't be the same.
advanceElapsedClock(MINUTE_IN_MILLIS);
@@ -1037,8 +1130,11 @@ public class QuotaControllerTest {
expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod;
expectedStats.sessionCountInWindow = originalStatsActive.sessionCountInWindow;
expectedStats.inQuotaTimeElapsed = originalStatsActive.inQuotaTimeElapsed;
- final ExecutionStats newStatsActive = mQuotaController.getExecutionStatsLocked(0,
- "com.android.test", ACTIVE_INDEX);
+ final ExecutionStats newStatsActive;
+ synchronized (mQuotaController.mLock) {
+ newStatsActive = mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX);
+ }
// Stats for the same bucket should use the same object.
assertTrue(originalStatsActive == newStatsActive);
assertEquals(expectedStats, newStatsActive);
@@ -1051,8 +1147,11 @@ public class QuotaControllerTest {
expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
expectedStats.sessionCountInWindow = originalStatsWorking.sessionCountInWindow;
expectedStats.inQuotaTimeElapsed = originalStatsWorking.inQuotaTimeElapsed;
- final ExecutionStats newStatsWorking = mQuotaController.getExecutionStatsLocked(0,
- "com.android.test", WORKING_INDEX);
+ final ExecutionStats newStatsWorking;
+ synchronized (mQuotaController.mLock) {
+ newStatsWorking = mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX);
+ }
assertTrue(originalStatsWorking == newStatsWorking);
assertNotEquals(expectedStats, newStatsWorking);
@@ -1064,8 +1163,11 @@ public class QuotaControllerTest {
expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
expectedStats.sessionCountInWindow = originalStatsFrequent.sessionCountInWindow;
expectedStats.inQuotaTimeElapsed = originalStatsFrequent.inQuotaTimeElapsed;
- final ExecutionStats newStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
- "com.android.test", FREQUENT_INDEX);
+ final ExecutionStats newStatsFrequent;
+ synchronized (mQuotaController.mLock) {
+ newStatsFrequent = mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX);
+ }
assertTrue(originalStatsFrequent == newStatsFrequent);
assertNotEquals(expectedStats, newStatsFrequent);
@@ -1077,8 +1179,11 @@ public class QuotaControllerTest {
expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
expectedStats.sessionCountInWindow = originalStatsRare.sessionCountInWindow;
expectedStats.inQuotaTimeElapsed = originalStatsRare.inQuotaTimeElapsed;
- final ExecutionStats newStatsRare = mQuotaController.getExecutionStatsLocked(0,
- "com.android.test", RARE_INDEX);
+ final ExecutionStats newStatsRare;
+ synchronized (mQuotaController.mLock) {
+ newStatsRare = mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX);
+ }
assertTrue(originalStatsRare == newStatsRare);
assertEquals(expectedStats, newStatsRare);
}
@@ -1092,26 +1197,36 @@ public class QuotaControllerTest {
job.setStandbyBucket(RARE_INDEX);
setCharging();
- assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
- mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+ }
setDischarging();
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
- mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+ }
// Top-started job
setProcessState(ActivityManager.PROCESS_STATE_TOP);
- mQuotaController.maybeStartTrackingJobLocked(job, null);
- mQuotaController.prepareForExecutionLocked(job);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+ mQuotaController.prepareForExecutionLocked(job);
+ }
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
- assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
- mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
- mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+ mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+ }
setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
}
/**
@@ -1137,30 +1252,46 @@ public class QuotaControllerTest {
createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
setStandbyBucket(RARE_INDEX);
- assertEquals(30 * SECOND_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(30 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
setStandbyBucket(FREQUENT_INDEX);
- assertEquals(MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
setStandbyBucket(WORKING_INDEX);
- assertEquals(5 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(5 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
// ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
// max execution time.
setStandbyBucket(ACTIVE_INDEX);
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
}
/**
@@ -1175,11 +1306,15 @@ public class QuotaControllerTest {
now - (24 * HOUR_IN_MILLIS + 8 * MINUTE_IN_MILLIS), 4 * HOUR_IN_MILLIS, 5));
setStandbyBucket(WORKING_INDEX);
- assertEquals(8 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- // Max time will phase out, so should use bucket limit.
- assertEquals(10 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(8 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ // Max time will phase out, so should use bucket limit.
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
// Close to boundary.
@@ -1188,10 +1323,14 @@ public class QuotaControllerTest {
4 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS, 5));
setStandbyBucket(WORKING_INDEX);
- assertEquals(5 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(10 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(5 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
// Far from boundary.
@@ -1200,10 +1339,14 @@ public class QuotaControllerTest {
now - (20 * HOUR_IN_MILLIS), 4 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS, 5));
setStandbyBucket(WORKING_INDEX);
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(3 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(3 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
}
/**
@@ -1225,13 +1368,17 @@ public class QuotaControllerTest {
createTimingSession(
now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
- // Both max and bucket time have 8 minutes left.
- assertEquals(8 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- // Max time essentially free. Bucket time has 2 min phase out plus original 8 minute
- // window time.
- assertEquals(10 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ // Both max and bucket time have 8 minutes left.
+ assertEquals(8 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ // Max time essentially free. Bucket time has 2 min phase out plus original 8 minute
+ // window time.
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
// Overlap boundary.
@@ -1247,23 +1394,32 @@ public class QuotaControllerTest {
createTimingSession(
now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
- // Both max and bucket time have 8 minutes left.
- assertEquals(8 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
- // Max time only has one minute phase out. Bucket time has 2 minute phase out.
- assertEquals(9 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ // Both max and bucket time have 8 minutes left.
+ assertEquals(8 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ // Max time only has one minute phase out. Bucket time has 2 minute phase out.
+ assertEquals(9 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
}
@Test
public void testIsWithinQuotaLocked_NeverApp() {
- assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertFalse(
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX));
+ }
}
@Test
public void testIsWithinQuotaLocked_Charging() {
setCharging();
- assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
+ }
}
@Test
@@ -1275,7 +1431,9 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
mQuotaController.incrementJobCount(0, "com.android.test", 5);
- assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ }
}
@Test
@@ -1288,15 +1446,19 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(0, "com.android.test.spam",
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount));
mQuotaController.incrementJobCount(0, "com.android.test.spam", jobCount);
- assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.spam",
- WORKING_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test.spam", WORKING_INDEX));
+ }
mQuotaController.saveTimingSession(0, "com.android.test.frequent",
createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000));
mQuotaController.saveTimingSession(0, "com.android.test.frequent",
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500));
- assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.frequent",
- FREQUENT_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test.frequent", FREQUENT_INDEX));
+ }
}
@Test
@@ -1310,7 +1472,9 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5));
mQuotaController.incrementJobCount(0, "com.android.test", 5);
- assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ }
}
@Test
@@ -1323,7 +1487,9 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount));
mQuotaController.incrementJobCount(0, "com.android.test", jobCount);
- assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ }
}
@Test
@@ -1335,32 +1501,42 @@ public class QuotaControllerTest {
setStandbyBucket(ACTIVE_INDEX, jobStatus);
setProcessState(ActivityManager.PROCESS_STATE_BACKUP);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
for (int i = 0; i < 20; ++i) {
advanceElapsedClock(SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
}
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
advanceElapsedClock(15 * SECOND_IN_MILLIS);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
for (int i = 0; i < 20; ++i) {
advanceElapsedClock(SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
}
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
advanceElapsedClock(10 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS);
- assertEquals(2, mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInRateLimitingWindow);
- assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus));
- assertTrue(jobStatus.isReady());
+ synchronized (mQuotaController.mLock) {
+ assertEquals(2, mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX).jobCountInRateLimitingWindow);
+ assertTrue(mQuotaController.isWithinQuotaLocked(jobStatus));
+ assertTrue(jobStatus.isReady());
+ }
}
@Test
@@ -1397,44 +1573,54 @@ public class QuotaControllerTest {
doReturn(new String[]{fgChangerPkgName})
.when(packageManager).getPackagesForUid(fgChangerUid);
- mQuotaController.maybeStartTrackingJobLocked(unaffected, null);
- mQuotaController.prepareForExecutionLocked(unaffected);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(unaffected, null);
+ mQuotaController.prepareForExecutionLocked(unaffected);
- mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null);
- mQuotaController.prepareForExecutionLocked(fgStateChanger);
+ mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null);
+ mQuotaController.prepareForExecutionLocked(fgStateChanger);
+ }
for (int i = 0; i < 20; ++i) {
advanceElapsedClock(SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid);
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid);
}
- mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false);
+ }
advanceElapsedClock(15 * SECOND_IN_MILLIS);
- mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null);
- mQuotaController.prepareForExecutionLocked(fgStateChanger);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(fgStateChanger, null);
+ mQuotaController.prepareForExecutionLocked(fgStateChanger);
+ }
for (int i = 0; i < 20; ++i) {
advanceElapsedClock(SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_TOP, fgChangerUid);
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING, fgChangerUid);
}
- mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(fgStateChanger, null, false);
- mQuotaController.maybeStopTrackingJobLocked(unaffected, null, false);
+ mQuotaController.maybeStopTrackingJobLocked(unaffected, null, false);
- assertTrue(mQuotaController.isWithinQuotaLocked(unaffected));
- assertTrue(unaffected.isReady());
- assertFalse(mQuotaController.isWithinQuotaLocked(fgStateChanger));
- assertFalse(fgStateChanger.isReady());
+ assertTrue(mQuotaController.isWithinQuotaLocked(unaffected));
+ assertTrue(unaffected.isReady());
+ assertFalse(mQuotaController.isWithinQuotaLocked(fgStateChanger));
+ assertFalse(fgStateChanger.isReady());
+ }
assertEquals(1,
mQuotaController.getTimingSessions(SOURCE_USER_ID, unaffectedPkgName).size());
assertEquals(42,
mQuotaController.getTimingSessions(SOURCE_USER_ID, fgChangerPkgName).size());
- for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) {
- assertEquals(42, mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, fgChangerPkgName, i).jobCountInRateLimitingWindow);
- assertEquals(1, mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, unaffectedPkgName, i).jobCountInRateLimitingWindow);
+ synchronized (mQuotaController.mLock) {
+ for (int i = ACTIVE_INDEX; i < RARE_INDEX; ++i) {
+ assertEquals(42, mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, fgChangerPkgName, i).jobCountInRateLimitingWindow);
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, unaffectedPkgName, i).jobCountInRateLimitingWindow);
+ }
}
}
@@ -1453,25 +1639,30 @@ public class QuotaControllerTest {
2));
mQuotaController.incrementJobCount(0, "com.android.test", 2);
- assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
- i < 2,
- mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
- assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
- i < 3,
- mQuotaController.isWithinQuotaLocked(0, "com.android.test", FREQUENT_INDEX));
- assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
- i < 4,
- mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
- assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
- i < 5,
- mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX));
+ synchronized (mQuotaController.mLock) {
+ assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
+ i < 2,
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
+ assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
+ i < 3,
+ mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", FREQUENT_INDEX));
+ assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
+ i < 4,
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
+ i < 5,
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX));
+ }
}
}
@Test
public void testMaybeScheduleCleanupAlarmLocked() {
// No sessions saved yet.
- mQuotaController.maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleCleanupAlarmLocked();
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any());
// Test with only one timing session saved.
@@ -1479,7 +1670,9 @@ public class QuotaControllerTest {
final long end = now - (6 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
mQuotaController.saveTimingSession(0, "com.android.test",
new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1));
- mQuotaController.maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleCleanupAlarmLocked();
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
@@ -1488,7 +1681,9 @@ public class QuotaControllerTest {
createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleCleanupAlarmLocked();
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(end + 24 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
}
@@ -1505,8 +1700,10 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
// No sessions saved yet.
- mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
@@ -1519,27 +1716,35 @@ public class QuotaControllerTest {
createTimingSession(now - 12 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 7 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1);
setStandbyBucket(standbyBucket, jobStatus);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(5 * MINUTE_IN_MILLIS);
- // Timer has only been going for 5 minutes in the past 10 minutes, which is under the window
- // size limit, but the total execution time for the past 24 hours is 6 hours, so the job no
- // longer has quota.
- assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked(jobStatus));
- mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ // Timer has only been going for 5 minutes in the past 10 minutes, which is under the
+ // window size limit, but the total execution time for the past 24 hours is 6 hours, so
+ // the job no longer has quota.
+ assertEquals(0, mQuotaController.getRemainingExecutionTimeLocked(jobStatus));
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, times(1)).set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK),
any(), any());
}
@@ -1554,15 +1759,19 @@ public class QuotaControllerTest {
// Working set window size is 2 hours.
final int standbyBucket = WORKING_INDEX;
- // No sessions saved yet.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ // No sessions saved yet.
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions in window but still in quota.
@@ -1572,7 +1781,9 @@ public class QuotaControllerTest {
end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Add some more sessions, but still in quota.
@@ -1580,18 +1791,24 @@ public class QuotaControllerTest {
createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test when out of quota.
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
// Alarm already scheduled, so make sure it's not scheduled again.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
@@ -1607,14 +1824,18 @@ public class QuotaControllerTest {
final int standbyBucket = FREQUENT_INDEX;
// No sessions saved yet.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions in window but still in quota.
@@ -1622,7 +1843,9 @@ public class QuotaControllerTest {
final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Add some more sessions, but still in quota.
@@ -1630,18 +1853,24 @@ public class QuotaControllerTest {
createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test when out of quota.
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
// Alarm already scheduled, so make sure it's not scheduled again.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
@@ -1660,14 +1889,18 @@ public class QuotaControllerTest {
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 50);
// No sessions saved yet.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions in window but still in quota.
@@ -1679,7 +1912,9 @@ public class QuotaControllerTest {
+ mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Add some more sessions, but still in quota.
@@ -1687,18 +1922,24 @@ public class QuotaControllerTest {
createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test when out of quota.
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1));
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
// Alarm already scheduled, so make sure it's not scheduled again.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
@@ -1730,7 +1971,9 @@ public class QuotaControllerTest {
InOrder inOrder = inOrder(mAlarmManager);
// Start in ACTIVE bucket.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+ }
inOrder.verify(mAlarmManager, never())
.set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));
@@ -1739,34 +1982,46 @@ public class QuotaControllerTest {
final long expectedWorkingAlarmTime =
outOfQuotaTime + (2 * HOUR_IN_MILLIS)
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+ }
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
final long expectedFrequentAlarmTime =
outOfQuotaTime + (8 * HOUR_IN_MILLIS)
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+ }
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
final long expectedRareAlarmTime =
outOfQuotaTime + (24 * HOUR_IN_MILLIS)
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX);
+ }
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
// And back up again.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+ }
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+ }
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+ }
inOrder.verify(mAlarmManager, never())
.set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class));
@@ -1781,22 +2036,29 @@ public class QuotaControllerTest {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int standbyBucket = WORKING_INDEX;
- ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
- SOURCE_PACKAGE, standbyBucket);
+ ExecutionStats stats;
+ synchronized (mQuotaController.mLock) {
+ stats = mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
stats.jobCountInRateLimitingWindow =
mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW + 2;
// Invalid time in the past, so the count shouldn't be used.
stats.jobRateLimitExpirationTimeElapsed = now - 5 * MINUTE_IN_MILLIS / 2;
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Valid time in the future, so the count should be used.
stats.jobRateLimitExpirationTimeElapsed = now + 5 * MINUTE_IN_MILLIS / 2;
final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed;
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
@@ -1884,8 +2146,10 @@ public class QuotaControllerTest {
// is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS
+ (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs);
- mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
@@ -1915,8 +2179,10 @@ public class QuotaControllerTest {
final long expectedAlarmTime = now - 20 * HOUR_IN_MILLIS
+ 24 * HOUR_IN_MILLIS
+ (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs);
- mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
@@ -2070,13 +2336,19 @@ public class QuotaControllerTest {
setCharging();
JobStatus jobStatus = createJobStatus("testTimerTracking_Charging", 1);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(5 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
}
@@ -2087,41 +2359,63 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_BACKUP);
JobStatus jobStatus = createJobStatus("testTimerTracking_Discharging", 1);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
List<TimingSession> expected = new ArrayList<>();
long start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(5 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
// Test overlapping jobs.
JobStatus jobStatus2 = createJobStatus("testTimerTracking_Discharging", 2);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ }
JobStatus jobStatus3 = createJobStatus("testTimerTracking_Discharging", 3);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ }
advanceElapsedClock(SECOND_IN_MILLIS);
start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.prepareForExecutionLocked(jobStatus2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.prepareForExecutionLocked(jobStatus3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ }
advanceElapsedClock(20 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ }
expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
}
@@ -2135,11 +2429,17 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
JobStatus jobStatus = createJobStatus("testTimerTracking_ChargingAndDischarging", 1);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
JobStatus jobStatus2 = createJobStatus("testTimerTracking_ChargingAndDischarging", 2);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ }
JobStatus jobStatus3 = createJobStatus("testTimerTracking_ChargingAndDischarging", 3);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
List<TimingSession> expected = new ArrayList<>();
@@ -2147,12 +2447,16 @@ public class QuotaControllerTest {
// should be counted.
setCharging();
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
setDischarging();
long start = JobSchedulerService.sElapsedRealtimeClock.millis();
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true);
+ }
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -2166,23 +2470,35 @@ public class QuotaControllerTest {
// shouldn't be included in either job count.
setDischarging();
start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.prepareForExecutionLocked(jobStatus2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
setCharging();
expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
- mQuotaController.prepareForExecutionLocked(jobStatus3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
setDischarging();
start = JobSchedulerService.sElapsedRealtimeClock.millis();
advanceElapsedClock(20 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -2190,13 +2506,17 @@ public class QuotaControllerTest {
// during the discharging period should be counted.
setDischarging();
start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
- mQuotaController.prepareForExecutionLocked(jobStatus2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
setCharging();
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ }
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
}
@@ -2207,42 +2527,63 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
JobStatus jobStatus = createJobStatus("testTimerTracking_AllBackground", 1);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
-
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
List<TimingSession> expected = new ArrayList<>();
// Test single job.
long start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(5 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
// Test overlapping jobs.
JobStatus jobStatus2 = createJobStatus("testTimerTracking_AllBackground", 2);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ }
JobStatus jobStatus3 = createJobStatus("testTimerTracking_AllBackground", 3);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ }
advanceElapsedClock(SECOND_IN_MILLIS);
start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.prepareForExecutionLocked(jobStatus2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.prepareForExecutionLocked(jobStatus3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ }
advanceElapsedClock(20 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ }
expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
}
@@ -2254,16 +2595,22 @@ public class QuotaControllerTest {
JobStatus jobStatus = createJobStatus("testTimerTracking_AllForeground", 1);
setProcessState(ActivityManager.PROCESS_STATE_TOP);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(5 * SECOND_IN_MILLIS);
// Change to a state that should still be considered foreground.
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
advanceElapsedClock(5 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
}
@@ -2278,18 +2625,24 @@ public class QuotaControllerTest {
JobStatus jobBg1 = createJobStatus("testTimerTracking_ForegroundAndBackground", 1);
JobStatus jobBg2 = createJobStatus("testTimerTracking_ForegroundAndBackground", 2);
JobStatus jobFg3 = createJobStatus("testTimerTracking_ForegroundAndBackground", 3);
- mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
- mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
- mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
List<TimingSession> expected = new ArrayList<>();
// UID starts out inactive.
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
long start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.prepareForExecutionLocked(jobBg1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ }
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -2301,16 +2654,24 @@ public class QuotaControllerTest {
// App remains in foreground state after coming to foreground, so there should only be one
// session.
start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
- mQuotaController.prepareForExecutionLocked(jobBg2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- mQuotaController.prepareForExecutionLocked(jobFg3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobFg3);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ }
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
advanceElapsedClock(SECOND_IN_MILLIS);
@@ -2321,25 +2682,39 @@ public class QuotaControllerTest {
// * The first should have a count of 1
// * The second should have a count of 2 since it will include both jobs
start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
- mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
- mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+ }
setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
- mQuotaController.prepareForExecutionLocked(jobBg1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- mQuotaController.prepareForExecutionLocked(jobFg3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobFg3);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
start = JobSchedulerService.sElapsedRealtimeClock.millis();
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
- mQuotaController.prepareForExecutionLocked(jobBg2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ }
expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
}
@@ -2355,21 +2730,34 @@ public class QuotaControllerTest {
JobStatus jobFg1 = createJobStatus("testTimerTracking_JobCount_Foreground", 1);
JobStatus jobFg2 = createJobStatus("testTimerTracking_JobCount_Foreground", 2);
- mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
- mQuotaController.maybeStartTrackingJobLocked(jobFg2, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg2, null);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
- ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
- SOURCE_PACKAGE, standbyBucket);
+ ExecutionStats stats;
+ synchronized (mQuotaController.mLock) {
+ stats = mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
assertEquals(0, stats.jobCountInRateLimitingWindow);
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- mQuotaController.prepareForExecutionLocked(jobFg1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobFg1);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.prepareForExecutionLocked(jobFg2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobFg2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(0, stats.jobCountInRateLimitingWindow);
@@ -2383,21 +2771,32 @@ public class QuotaControllerTest {
final int standbyBucket = WORKING_INDEX;
JobStatus jobBg1 = createJobStatus("testTimerTracking_JobCount_Background", 1);
JobStatus jobBg2 = createJobStatus("testTimerTracking_JobCount_Background", 2);
- mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
- mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ ExecutionStats stats;
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
- ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
- SOURCE_PACKAGE, standbyBucket);
+ stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
+ SOURCE_PACKAGE, standbyBucket);
+ }
assertEquals(0, stats.jobCountInRateLimitingWindow);
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
- mQuotaController.prepareForExecutionLocked(jobBg1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.prepareForExecutionLocked(jobBg2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg1, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ }
assertEquals(2, stats.jobCountInRateLimitingWindow);
}
@@ -2413,19 +2812,25 @@ public class QuotaControllerTest {
JobStatus jobBg2 = createJobStatus("testTimerTracking_TopAndNonTop", 2);
JobStatus jobFg1 = createJobStatus("testTimerTracking_TopAndNonTop", 3);
JobStatus jobTop = createJobStatus("testTimerTracking_TopAndNonTop", 4);
- mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
- mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
- mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
- mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+ }
assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
List<TimingSession> expected = new ArrayList<>();
// UID starts out inactive.
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
long start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.prepareForExecutionLocked(jobBg1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ }
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
@@ -2437,16 +2842,24 @@ public class QuotaControllerTest {
// App remains in top state after coming to top, so there should only be one
// session.
start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
- mQuotaController.prepareForExecutionLocked(jobBg2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
setProcessState(ActivityManager.PROCESS_STATE_TOP);
- mQuotaController.prepareForExecutionLocked(jobTop);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ }
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
advanceElapsedClock(SECOND_IN_MILLIS);
@@ -2459,31 +2872,47 @@ public class QuotaControllerTest {
// * The second should have a count of 2, which accounts for the bg2 and fg, but not top
// jobs.
start = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
- mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
- mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+ }
setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
- mQuotaController.prepareForExecutionLocked(jobBg1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
setProcessState(ActivityManager.PROCESS_STATE_TOP);
- mQuotaController.prepareForExecutionLocked(jobTop);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ }
advanceElapsedClock(5 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- mQuotaController.prepareForExecutionLocked(jobFg1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobFg1);
+ }
advanceElapsedClock(5 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_TOP);
advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
start = JobSchedulerService.sElapsedRealtimeClock.millis();
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
- mQuotaController.prepareForExecutionLocked(jobBg2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+ }
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+ }
expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
}
@@ -2511,20 +2940,28 @@ public class QuotaControllerTest {
// UID starts out inactive.
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
// Start the job.
- mQuotaController.prepareForExecutionLocked(jobBg);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg);
+ }
advanceElapsedClock(remainingTimeMs / 2);
// New job starts after UID is in the foreground. Since the app is now in the foreground, it
// should continue to have remainingTimeMs / 2 time remaining.
setProcessState(ActivityManager.PROCESS_STATE_TOP);
- mQuotaController.prepareForExecutionLocked(jobTop);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
advanceElapsedClock(remainingTimeMs);
// Wait for some extra time to allow for job processing.
inOrder.verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
.onControllerStateChanged();
- assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobBg));
- assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobTop));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(remainingTimeMs / 2,
+ mQuotaController.getRemainingExecutionTimeLocked(jobBg));
+ assertEquals(remainingTimeMs / 2,
+ mQuotaController.getRemainingExecutionTimeLocked(jobTop));
+ }
// Go to a background state.
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
advanceElapsedClock(remainingTimeMs / 2 + 1);
@@ -2546,7 +2983,9 @@ public class QuotaControllerTest {
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
.onControllerStateChanged();
trackJobs(jobFg, jobTop);
- mQuotaController.prepareForExecutionLocked(jobTop);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -2578,7 +3017,9 @@ public class QuotaControllerTest {
@Test
public void testTracking_OutOfQuota() {
JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
setProcessState(ActivityManager.PROCESS_STATE_HOME);
// Now the package only has two seconds to run.
@@ -2589,7 +3030,9 @@ public class QuotaControllerTest {
10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
// Start the job.
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(remainingTimeMs);
// Wait for some extra time to allow for job processing.
@@ -2608,7 +3051,9 @@ public class QuotaControllerTest {
@Test
public void testTracking_RollingQuota() {
JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
Handler handler = mQuotaController.getHandler();
@@ -2625,9 +3070,13 @@ public class QuotaControllerTest {
createTimingSession(now - HOUR_IN_MILLIS,
9 * MINUTE_IN_MILLIS + 50 * SECOND_IN_MILLIS, 1));
- assertEquals(remainingTimeMs, mQuotaController.getRemainingExecutionTimeLocked(jobStatus));
- // Start the job.
- mQuotaController.prepareForExecutionLocked(jobStatus);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(remainingTimeMs,
+ mQuotaController.getRemainingExecutionTimeLocked(jobStatus));
+
+ // Start the job.
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
advanceElapsedClock(remainingTimeMs);
// Wait for some extra time to allow for job processing.
@@ -2638,8 +3087,11 @@ public class QuotaControllerTest {
// The job used up the remaining quota, but in that time, the same amount of time in the
// old TimingSession also fell out of the quota window, so it should still have the same
// amount of remaining time left its quota.
- assertEquals(remainingTimeMs,
- mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ synchronized (mQuotaController.mLock) {
+ assertEquals(remainingTimeMs,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
// Handler is told to check when the quota will be consumed, not when the initial
// remaining time is over.
verify(handler, atLeast(1)).sendMessageDelayed(any(), eq(10 * SECOND_IN_MILLIS));
@@ -2675,19 +3127,25 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
// No sessions saved yet.
- mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Ran jobs up to the job limit. All of them should be allowed to run.
for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW; ++i) {
JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i);
setStandbyBucket(WORKING_INDEX, job);
- mQuotaController.maybeStartTrackingJobLocked(job, null);
- assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
- mQuotaController.prepareForExecutionLocked(job);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+ assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ mQuotaController.prepareForExecutionLocked(job);
+ }
advanceElapsedClock(SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+ }
advanceElapsedClock(SECOND_IN_MILLIS);
}
// Start alarm shouldn't have been scheduled since the app was in quota up until this point.
@@ -2697,11 +3155,16 @@ public class QuotaControllerTest {
JobStatus throttledJob = createJobStatus(
"testStartAlarmScheduled_JobCount_AllowedTime", 42);
setStandbyBucket(WORKING_INDEX, throttledJob);
- mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
+ }
assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
- ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
- SOURCE_PACKAGE, standbyBucket);
+ ExecutionStats stats;
+ synchronized (mQuotaController.mLock) {
+ stats = mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
final long expectedWorkingAlarmTime = stats.jobRateLimitExpirationTimeElapsed;
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2730,8 +3193,10 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
// No sessions saved yet.
- mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
- standbyBucket);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Ran jobs up to the job limit. All of them should be allowed to run.
@@ -2739,12 +3204,16 @@ public class QuotaControllerTest {
JobStatus job = createJobStatus(
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", i);
setStandbyBucket(FREQUENT_INDEX, job);
- mQuotaController.maybeStartTrackingJobLocked(job, null);
- assertTrue("Constraint not satisfied for job #" + (i + 1),
- job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
- mQuotaController.prepareForExecutionLocked(job);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+ assertTrue("Constraint not satisfied for job #" + (i + 1),
+ job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ mQuotaController.prepareForExecutionLocked(job);
+ }
advanceElapsedClock(SECOND_IN_MILLIS);
- mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+ }
advanceElapsedClock(SECOND_IN_MILLIS);
}
// Start alarm shouldn't have been scheduled since the app was in quota up until this point.
@@ -2753,13 +3222,18 @@ public class QuotaControllerTest {
// The app is now out of session count quota
JobStatus throttledJob = createJobStatus(
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", 42);
- mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
+ }
assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(),
throttledJob.getWhenStandbyDeferred());
- ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
- SOURCE_PACKAGE, standbyBucket);
+ ExecutionStats stats;
+ synchronized (mQuotaController.mLock) {
+ stats = mQuotaController.getExecutionStatsLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
final long expectedWorkingAlarmTime = stats.sessionRateLimitExpirationTimeElapsed;
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
index 3614763fecab..9d847153661f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
@@ -52,6 +52,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
@@ -632,6 +633,50 @@ public class TimeControllerTest {
}
@Test
+ public void testDelayAlarmSchedulingCoalescedIntervals() {
+ doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
+
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+ JobStatus jobLatest = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals",
+ createJob().setMinimumLatency(HOUR_IN_MILLIS));
+ JobStatus jobMiddle = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals",
+ createJob().setMinimumLatency(TimeController.DELAY_COALESCE_TIME_MS / 2));
+ JobStatus jobEarliest = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals",
+ createJob().setMinimumLatency(TimeController.DELAY_COALESCE_TIME_MS / 10));
+
+ ArgumentCaptor<AlarmManager.OnAlarmListener> listenerCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
+ mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
+ mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(now + TimeController.DELAY_COALESCE_TIME_MS / 10), anyLong(),
+ anyLong(), eq(TAG_DELAY),
+ listenerCaptor.capture(), any(), any());
+ final AlarmManager.OnAlarmListener delayListener = listenerCaptor.getValue();
+
+ advanceElapsedClock(TimeController.DELAY_COALESCE_TIME_MS / 10);
+ delayListener.onAlarm();
+ // The next delay alarm time should be TimeController.DELAY_COALESCE_TIME_MS after the last
+ // time the delay alarm fired.
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(now + TimeController.DELAY_COALESCE_TIME_MS / 10
+ + TimeController.DELAY_COALESCE_TIME_MS), anyLong(),
+ anyLong(), eq(TAG_DELAY), any(), any(), any());
+
+ advanceElapsedClock(TimeController.DELAY_COALESCE_TIME_MS);
+ delayListener.onAlarm();
+ // The last job is significantly after the coalesce time, so the 3rd scheduling shouldn't be
+ // affected by the first two jobs' alarms.
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(),
+ anyLong(), eq(TAG_DELAY), any(), any(), any());
+ }
+
+ @Test
public void testEvaluateStateLocked_Delay() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index 3aedd3c7d753..d260e4dce79a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -22,6 +22,7 @@ import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.location.Criteria.ACCURACY_COARSE;
import static android.location.Criteria.ACCURACY_FINE;
import static android.location.Criteria.POWER_HIGH;
+import static android.location.LocationRequest.PASSIVE_INTERVAL;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
import static androidx.test.ext.truth.location.LocationSubject.assertThat;
@@ -598,6 +599,38 @@ public class LocationProviderManagerTest {
}
@Test
+ public void testRegisterListener_Coarse() throws Exception {
+ ILocationListener listener = createMockLocationListener();
+ mManager.registerLocationRequest(
+ new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+ IDENTITY,
+ PERMISSION_COARSE,
+ listener);
+
+ mProvider.setProviderLocation(createLocation(NAME, mRandom));
+ mProvider.setProviderLocation(createLocation(NAME, mRandom));
+ verify(listener, times(1))
+ .onLocationChanged(any(Location.class), nullable(IRemoteCallback.class));
+ }
+
+ @Test
+ public void testRegisterListener_Coarse_Passive() throws Exception {
+ ILocationListener listener = createMockLocationListener();
+ mManager.registerLocationRequest(
+ new LocationRequest.Builder(PASSIVE_INTERVAL)
+ .setMinUpdateIntervalMillis(0)
+ .setWorkSource(WORK_SOURCE).build(),
+ IDENTITY,
+ PERMISSION_COARSE,
+ listener);
+
+ mProvider.setProviderLocation(createLocation(NAME, mRandom));
+ mProvider.setProviderLocation(createLocation(NAME, mRandom));
+ verify(listener, times(1))
+ .onLocationChanged(any(Location.class), nullable(IRemoteCallback.class));
+ }
+
+ @Test
public void testGetCurrentLocation() throws Exception {
ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 8d60de9abe99..fba321e07207 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -52,6 +52,8 @@ import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -76,7 +78,11 @@ import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Tests for {@link VibratorService}.
@@ -444,6 +450,42 @@ public class VibratorServiceTest {
}
@Test
+ public void vibrate_withWaveform_totalVibrationTimeRespected() throws Exception {
+ int totalDuration = 10_000; // 10s
+ int stepDuration = 25; // 25ms
+
+ // 25% of the first waveform step will be spent on the native on() call.
+ mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ doAnswer(invocation -> {
+ Thread.currentThread().sleep(stepDuration / 4);
+ return null;
+ }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong());
+ // 25% of each waveform step will be spent on the native setAmplitude() call..
+ doAnswer(invocation -> {
+ Thread.currentThread().sleep(stepDuration / 4);
+ return null;
+ }).when(mNativeWrapperMock).vibratorSetAmplitude(anyInt());
+
+ VibratorService service = createService();
+
+ int stepCount = totalDuration / stepDuration;
+ long[] timings = new long[stepCount];
+ int[] amplitudes = new int[stepCount];
+ Arrays.fill(timings, stepDuration);
+ Arrays.fill(amplitudes, VibrationEffect.DEFAULT_AMPLITUDE);
+ VibrationEffect effect = VibrationEffect.createWaveform(timings, amplitudes, -1);
+
+ int perceivedDuration = vibrateAndMeasure(service, effect, /* timeoutSecs= */ 15);
+ int delay = Math.abs(perceivedDuration - totalDuration);
+
+ // Allow some delay for thread scheduling and callback triggering.
+ int maxDelay = (int) (0.05 * totalDuration); // < 5% of total duration
+ assertTrue("Waveform with perceived delay of " + delay + "ms,"
+ + " expected less than " + maxDelay + "ms",
+ delay < maxDelay);
+ }
+
+ @Test
public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() {
VibratorService service = createService();
doAnswer(invocation -> {
@@ -699,16 +741,41 @@ public class VibratorServiceTest {
vibrate(service, effect, ALARM_ATTRS);
}
- private void vibrate(VibratorService service, VibrationEffect effect, AudioAttributes attrs) {
- VibrationAttributes attributes = new VibrationAttributes.Builder(attrs, effect).build();
- vibrate(service, effect, attributes);
- }
-
private void vibrate(VibratorService service, VibrationEffect effect,
VibrationAttributes attributes) {
service.vibrate(UID, PACKAGE_NAME, effect, attributes, "some reason", service);
}
+ private int vibrateAndMeasure(
+ VibratorService service, VibrationEffect effect, long timeoutSecs) throws Exception {
+ AtomicLong startTime = new AtomicLong(0);
+ AtomicLong endTime = new AtomicLong(0);
+ CountDownLatch startedCount = new CountDownLatch(1);
+ CountDownLatch finishedCount = new CountDownLatch(1);
+ service.registerVibratorStateListener(new IVibratorStateListener() {
+ @Override
+ public void onVibrating(boolean vibrating) throws RemoteException {
+ if (vibrating) {
+ startTime.set(SystemClock.uptimeMillis());
+ startedCount.countDown();
+ } else if (startedCount.getCount() == 0) {
+ endTime.set(SystemClock.uptimeMillis());
+ finishedCount.countDown();
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mVibratorStateListenerBinderMock;
+ }
+ });
+
+ vibrate(service, effect);
+
+ assertTrue(finishedCount.await(timeoutSecs, TimeUnit.SECONDS));
+ return (int) (endTime.get() - startTime.get());
+ }
+
private void mockVibratorCapabilities(int capabilities) {
when(mNativeWrapperMock.vibratorGetCapabilities()).thenReturn((long) capabilities);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 377bfd1170a6..24f8eabdf545 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -18,17 +18,25 @@ package com.android.server.am;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.server.LocalServices;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -42,6 +50,18 @@ import java.io.File;
@FlakyTest(bugId = 113616538)
public class AppErrorDialogTest {
+ @BeforeClass
+ public static void setUpOnce() {
+ final PackageManagerInternal pm = mock(PackageManagerInternal.class);
+ doReturn(new ComponentName("", "")).when(pm).getSystemUiServiceComponent();
+ LocalServices.addService(PackageManagerInternal.class, pm);
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ }
+
@Rule
public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
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 b8dbd6267bc5..325ba118c711 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
@@ -33,8 +35,6 @@ import com.android.server.display.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.DisplayModeDirector.Vote;
-import com.google.common.truth.Truth;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -90,9 +90,9 @@ public class DisplayModeDirectorTest {
// With no votes present, DisplayModeDirector should allow any refresh rate.
DesiredDisplayModeSpecs modeSpecs =
createDirectorFromFpsRange(60, 90).getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(modeSpecs.baseModeId).isEqualTo(60);
- Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f);
- Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY);
+ assertThat(modeSpecs.baseModeId).isEqualTo(60);
+ assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f);
+ assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY);
int numPriorities =
DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1;
@@ -112,10 +112,10 @@ public class DisplayModeDirectorTest {
votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i));
director.injectVotesByDisplay(votesByDisplay);
modeSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(modeSpecs.baseModeId).isEqualTo(minFps + i);
- Truth.assertThat(modeSpecs.primaryRefreshRateRange.min)
+ assertThat(modeSpecs.baseModeId).isEqualTo(minFps + i);
+ assertThat(modeSpecs.primaryRefreshRateRange.min)
.isEqualTo((float) (minFps + i));
- Truth.assertThat(modeSpecs.primaryRefreshRateRange.max)
+ assertThat(modeSpecs.primaryRefreshRateRange.max)
.isEqualTo((float) (maxFps - i));
}
}
@@ -132,9 +132,9 @@ public class DisplayModeDirectorTest {
votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80));
director.injectVotesByDisplay(votesByDisplay);
modeSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(modeSpecs.baseModeId).isEqualTo(70);
- Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(70f);
- Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(80f);
+ assertThat(modeSpecs.baseModeId).isEqualTo(70);
+ assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(70f);
+ assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(80f);
}
}
@@ -153,9 +153,9 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(60);
}
@Test
@@ -172,32 +172,32 @@ public class DisplayModeDirectorTest {
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
}
@Test
@@ -219,29 +219,29 @@ public class DisplayModeDirectorTest {
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
- Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
Vote.forRefreshRates(90, Float.POSITIVE_INFINITY));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f);
- Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
- Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(75, 75));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75);
- Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min)
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min)
.isWithin(FLOAT_TOLERANCE)
.of(75);
- Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max)
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max)
.isWithin(FLOAT_TOLERANCE)
.of(75);
}
@@ -251,10 +251,10 @@ public class DisplayModeDirectorTest {
float appRequestMin, float appRequestMax) {
DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecsWithInjectedFpsSettings(
minFps, peakFps, defaultFps);
- Truth.assertThat(specs.primaryRefreshRateRange.min).isEqualTo(primaryMin);
- Truth.assertThat(specs.primaryRefreshRateRange.max).isEqualTo(primaryMax);
- Truth.assertThat(specs.appRequestRefreshRateRange.min).isEqualTo(appRequestMin);
- Truth.assertThat(specs.appRequestRefreshRateRange.max).isEqualTo(appRequestMax);
+ assertThat(specs.primaryRefreshRateRange.min).isEqualTo(primaryMin);
+ assertThat(specs.primaryRefreshRateRange.max).isEqualTo(primaryMax);
+ assertThat(specs.appRequestRefreshRateRange.min).isEqualTo(appRequestMin);
+ assertThat(specs.appRequestRefreshRateRange.max).isEqualTo(appRequestMax);
}
@Test
@@ -318,26 +318,84 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
- Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
+ assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(60);
director.setShouldAlwaysRespectAppRequestedMode(true);
- Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue();
+ assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue();
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
- Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(90);
director.setShouldAlwaysRespectAppRequestedMode(false);
- Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
+ assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
+
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+ }
+
+ @Test
+ public void testVotingWithSwitchingTypeNone() {
+ final int displayId = 0;
+ DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(displayId, votes);
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(30, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
+
+
+ director.injectVotesByDisplay(votesByDisplay);
+ assertThat(director.getModeSwitchingType())
+ .isNotEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(30);
+
+ director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_NONE);
+ assertThat(director.getModeSwitchingType())
+ .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
- Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+ assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(30);
+ }
+
+ @Test
+ public void testVotingWithSwitchingTypeWithinGroups() {
+ final int displayId = 0;
+ DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
+
+ director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS);
+ assertThat(director.getModeSwitchingType())
+ .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ assertThat(desiredSpecs.allowGroupSwitching).isFalse();
+ }
+
+ @Test
+ public void testVotingWithSwitchingTypeWithinAndAcrossGroups() {
+ final int displayId = 0;
+ DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
+
+ director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
+ assertThat(director.getModeSwitchingType())
+ .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ assertThat(desiredSpecs.allowGroupSwitching).isTrue();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index a1eb0378a210..ae9c6188619f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -26,26 +26,16 @@ import android.hardware.hdmi.HdmiControlManager;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
-import android.util.Slog;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import com.android.server.hdmi.cec.config.CecSettings;
-import com.android.server.hdmi.cec.config.XmlParser;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import javax.xml.datatype.DatatypeConfigurationException;
@SmallTest
@Presubmit
@@ -65,13 +55,15 @@ public final class HdmiCecConfigTest {
@Test
public void getAllCecSettings_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
}
@Test
public void getAllCecSettings_Empty() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
@@ -80,7 +72,8 @@ public final class HdmiCecConfigTest {
@Test
public void getAllCecSettings_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"hdmi_cec_enabled\""
@@ -108,13 +101,15 @@ public final class HdmiCecConfigTest {
@Test
public void getUserCecSettings_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
}
@Test
public void getUserCecSettings_Empty() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
@@ -123,7 +118,8 @@ public final class HdmiCecConfigTest {
@Test
public void getUserCecSettings_OnlyMasterXml() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"hdmi_cec_enabled\""
@@ -151,7 +147,8 @@ public final class HdmiCecConfigTest {
@Test
public void getUserCecSettings_WithOverride() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"hdmi_cec_enabled\""
@@ -190,14 +187,16 @@ public final class HdmiCecConfigTest {
@Test
public void getAllowedValues_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getAllowedValues("foo"));
}
@Test
public void getAllowedValues_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
@@ -207,7 +206,8 @@ public final class HdmiCecConfigTest {
@Test
public void getAllowedValues_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
@@ -229,14 +229,16 @@ public final class HdmiCecConfigTest {
@Test
public void getDefaultValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getDefaultValue("foo"));
}
@Test
public void getDefaultValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
@@ -246,7 +248,8 @@ public final class HdmiCecConfigTest {
@Test
public void getDefaultValue_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
@@ -266,14 +269,16 @@ public final class HdmiCecConfigTest {
@Test
public void getValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getValue("foo"));
}
@Test
public void getValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
@@ -287,7 +292,8 @@ public final class HdmiCecConfigTest {
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV))
.thenReturn(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
@@ -313,7 +319,8 @@ public final class HdmiCecConfigTest {
.NONE.name().toLowerCase()))
.thenReturn(HdmiProperties.power_state_change_on_active_source_lost_values
.STANDBY_NOW.name().toLowerCase());
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"power_state_change_on_active_source_lost\""
@@ -333,14 +340,16 @@ public final class HdmiCecConfigTest {
@Test
public void setValue_NoMasterXml() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setValue("foo", "bar"));
}
@Test
public void setValue_InvalidSetting() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
@@ -350,7 +359,8 @@ public final class HdmiCecConfigTest {
@Test
public void setValue_NotConfigurable() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
@@ -371,7 +381,8 @@ public final class HdmiCecConfigTest {
@Test
public void setValue_InvalidValue() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
@@ -392,7 +403,8 @@ public final class HdmiCecConfigTest {
@Test
public void setValue_GlobalSetting_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
@@ -414,7 +426,8 @@ public final class HdmiCecConfigTest {
@Test
public void setValue_SystemProperty_BasicSanity() {
- HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"power_state_change_on_active_source_lost\""
@@ -435,22 +448,4 @@ public final class HdmiCecConfigTest {
HdmiProperties.power_state_change_on_active_source_lost_values
.STANDBY_NOW.name().toLowerCase());
}
-
- private HdmiCecConfig createHdmiCecConfig(String productConfigXml, String vendorOverrideXml) {
- CecSettings productConfig = null;
- CecSettings vendorOverride = null;
- try {
- if (productConfigXml != null) {
- productConfig = XmlParser.read(
- new ByteArrayInputStream(productConfigXml.getBytes()));
- }
- if (vendorOverrideXml != null) {
- vendorOverride = XmlParser.read(
- new ByteArrayInputStream(vendorOverrideXml.getBytes()));
- }
- } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
- Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
- }
- return new HdmiCecConfig(mContext, mStorageAdapter, productConfig, vendorOverride);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 6e7ec2a88140..81c3be5f9f75 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -20,6 +20,8 @@ import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_BACKUP_1;
+import static com.android.server.hdmi.Constants.ADDR_BACKUP_2;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_3;
@@ -68,9 +70,15 @@ public class HdmiCecControllerTest {
Looper getServiceLooper() {
return mMyLooper;
}
+
+ @Override
+ int getCecVersion() {
+ return mCecVersion;
+ }
}
private HdmiCecController mHdmiCecController;
+ private int mCecVersion = Constants.VERSION_1_4;
private int mLogicalAddress = 16;
private AllocateAddressCallback mCallback =
new AllocateAddressCallback() {
@@ -191,4 +199,61 @@ public class HdmiCecControllerTest {
mTestLooper.dispatchAll();
assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
}
+
+ @Test
+ public void testAllocateLogicalAddress_PlaybackNonPreferred_2_0_BackupOne() {
+ mCecVersion = Constants.VERSION_2_0;
+
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
+ mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
+ mTestLooper.dispatchAll();
+ assertEquals(ADDR_BACKUP_1, mLogicalAddress);
+ }
+
+ @Test
+ public void testAllocateLogicalAddress_PlaybackNonPreferred_2_0_BackupTwo() {
+ mCecVersion = Constants.VERSION_2_0;
+
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_BACKUP_1, SendMessageResult.SUCCESS);
+ mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
+ mTestLooper.dispatchAll();
+ assertEquals(ADDR_BACKUP_2, mLogicalAddress);
+ }
+
+ @Test
+ public void testAllocateLogicalAddress_PlaybackPreferredOccupiedDedicatedBelowAvailable() {
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
+ mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_2, mCallback);
+ mTestLooper.dispatchAll();
+ assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
+ }
+
+ @Test
+ public void testAllocateLogicalAddress_PlaybackPreferredOccupiedDedicatedAboveAvailable() {
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+ mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_2, mCallback);
+ mTestLooper.dispatchAll();
+ assertEquals(ADDR_PLAYBACK_3, mLogicalAddress);
+ }
+
+ @Test
+ public void testAllocateLogicalAddress_PlaybackNonPreferred_2_0_AllOccupied() {
+ mCecVersion = Constants.VERSION_2_0;
+
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_BACKUP_1, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_BACKUP_2, SendMessageResult.SUCCESS);
+ mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
+ mTestLooper.dispatchAll();
+ assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index debf20bffe28..470294073535 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -66,10 +66,67 @@ public class HdmiCecMessageValidatorTest {
@Test
public void isValid_reportPowerStatus() {
assertMessageValidity("04:90:00").isEqualTo(OK);
+ assertMessageValidity("04:90:03:05").isEqualTo(OK);
assertMessageValidity("0F:90:00").isEqualTo(ERROR_DESTINATION);
assertMessageValidity("F0:90").isEqualTo(ERROR_SOURCE);
assertMessageValidity("04:90").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("04:90:04").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_menuRequest() {
+ assertMessageValidity("40:8D:00").isEqualTo(OK);
+ assertMessageValidity("40:8D:02:04").isEqualTo(OK);
+
+ assertMessageValidity("0F:8D:00").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:8D").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:8D").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:8D:03").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_menuStatus() {
+ assertMessageValidity("40:8E:00").isEqualTo(OK);
+ assertMessageValidity("40:8E:01:00").isEqualTo(OK);
+
+ assertMessageValidity("0F:8E:00").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:8E").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:8E").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:8E:02").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_setSystemAudioMode() {
+ assertMessageValidity("40:72:00").isEqualTo(OK);
+ assertMessageValidity("4F:72:01:03").isEqualTo(OK);
+
+ assertMessageValidity("F0:72").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:72").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:72:02").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_systemAudioModeStatus() {
+ assertMessageValidity("40:7E:00").isEqualTo(OK);
+ assertMessageValidity("40:7E:01:01").isEqualTo(OK);
+
+ assertMessageValidity("0F:7E:00").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:7E").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:7E").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:7E:02").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_setAudioRate() {
+ assertMessageValidity("40:9A:00").isEqualTo(OK);
+ assertMessageValidity("40:9A:03").isEqualTo(OK);
+ assertMessageValidity("40:9A:06:02").isEqualTo(OK);
+
+ assertMessageValidity("0F:9A:00").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:9A").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:9A").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:9A:07").isEqualTo(ERROR_PARAMETER);
}
@Test
@@ -110,6 +167,21 @@ public class HdmiCecMessageValidatorTest {
assertMessageValidity("40:47:4C:69:7F").isEqualTo(ERROR_PARAMETER);
}
+ @Test
+ public void isValid_recordStatus() {
+ assertMessageValidity("40:0A:01").isEqualTo(OK);
+ assertMessageValidity("40:0A:13").isEqualTo(OK);
+ assertMessageValidity("40:0A:1F:04:01").isEqualTo(OK);
+
+ assertMessageValidity("0F:0A:01").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:0A:01").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:0A").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:0A:00").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:0A:0F").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:0A:1D").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:0A:30").isEqualTo(ERROR_PARAMETER);
+ }
+
private IntegerSubject assertMessageValidity(String message) {
return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 382ae8218729..68b46c408028 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -382,9 +382,13 @@ public class HdmiControlServiceTest {
assertThat(callback2.mVolumeControlEnabled).isTrue();
}
-
@Test
public void getCecVersion_default() {
+ // Set the Settings value to "null" to emulate it being empty and force the default value.
+ Settings.Global.putString(mContextSpy.getContentResolver(),
+ Settings.Global.HDMI_CEC_VERSION,
+ null);
+ mHdmiControlService.setControlEnabled(true);
assertThat(mHdmiControlService.getCecVersion()).isEqualTo(Constants.VERSION_1_4);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
index d8f5c4c8f58c..a8f3acf8726f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.hardware.hdmi.HdmiDeviceInfo;
import android.platform.test.annotations.Presubmit;
import android.util.Slog;
@@ -235,4 +236,366 @@ public class HdmiUtilsTest {
assertThat(HdmiUtils.pathRelationship(0x1234, 0x1234))
.isEqualTo(Constants.PATH_RELATIONSHIP_SAME);
}
+
+ @Test
+ public void getTypeFromAddress() {
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TV)).containsExactly(
+ HdmiDeviceInfo.DEVICE_TV);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_RECORDER_1)).containsExactly(
+ HdmiDeviceInfo.DEVICE_RECORDER);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_RECORDER_2)).containsExactly(
+ HdmiDeviceInfo.DEVICE_RECORDER);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TUNER_1)).containsExactly(
+ HdmiDeviceInfo.DEVICE_TUNER);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_PLAYBACK_1)).containsExactly(
+ HdmiDeviceInfo.DEVICE_PLAYBACK);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_AUDIO_SYSTEM)).containsExactly(
+ HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TUNER_2)).containsExactly(
+ HdmiDeviceInfo.DEVICE_TUNER);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TUNER_3)).containsExactly(
+ HdmiDeviceInfo.DEVICE_TUNER);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_PLAYBACK_2)).containsExactly(
+ HdmiDeviceInfo.DEVICE_PLAYBACK);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_RECORDER_3)).containsExactly(
+ HdmiDeviceInfo.DEVICE_RECORDER);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_TUNER_4)).containsExactly(
+ HdmiDeviceInfo.DEVICE_TUNER);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_PLAYBACK_3)).containsExactly(
+ HdmiDeviceInfo.DEVICE_PLAYBACK);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_BACKUP_1)).containsExactly(
+ HdmiDeviceInfo.DEVICE_PLAYBACK, HdmiDeviceInfo.DEVICE_RECORDER,
+ HdmiDeviceInfo.DEVICE_TUNER, HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_BACKUP_2)).containsExactly(
+ HdmiDeviceInfo.DEVICE_PLAYBACK, HdmiDeviceInfo.DEVICE_RECORDER,
+ HdmiDeviceInfo.DEVICE_TUNER, HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR);
+ assertThat(HdmiUtils.getTypeFromAddress(Constants.ADDR_SPECIFIC_USE)).containsExactly(
+ HdmiDeviceInfo.DEVICE_TV);
+ }
+
+ @Test
+ public void isEligibleAddressForDevice() {
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_TV)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_TV)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_TV)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_TV)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_TV)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_TV)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_TV)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_TV)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_RECORDER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_RECORDER_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_RECORDER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_RECORDER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_RECORDER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_RECORDER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_RECORDER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_RECORDER_1)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_RECORDER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_RECORDER_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_RECORDER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_RECORDER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_RECORDER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_RECORDER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_RECORDER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_RECORDER_2)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_TUNER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_TUNER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_TUNER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_TUNER_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_TUNER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_TUNER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_TUNER_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_TUNER_1)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_PLAYBACK_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_PLAYBACK_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_PLAYBACK_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_PLAYBACK_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_PLAYBACK_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_PLAYBACK_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_PLAYBACK_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_PLAYBACK_1)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_AUDIO_SYSTEM)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_AUDIO_SYSTEM)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_AUDIO_SYSTEM)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_AUDIO_SYSTEM)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_AUDIO_SYSTEM)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_AUDIO_SYSTEM)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_AUDIO_SYSTEM)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_AUDIO_SYSTEM)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_TUNER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_TUNER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_TUNER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_TUNER_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_TUNER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_TUNER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_TUNER_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_TUNER_2)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_TUNER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_TUNER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_TUNER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_TUNER_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_TUNER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_TUNER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_TUNER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_TUNER_3)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_PLAYBACK_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_PLAYBACK_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_PLAYBACK_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_PLAYBACK_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_PLAYBACK_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_PLAYBACK_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_PLAYBACK_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_PLAYBACK_2)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_RECORDER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_RECORDER_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_RECORDER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_RECORDER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_RECORDER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_RECORDER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_RECORDER_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_RECORDER_3)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_TUNER_4)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_TUNER_4)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_TUNER_4)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_TUNER_4)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_TUNER_4)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_TUNER_4)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_TUNER_4)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_TUNER_4)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_PLAYBACK_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_PLAYBACK_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_PLAYBACK_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_PLAYBACK_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_PLAYBACK_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_PLAYBACK_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_PLAYBACK_3)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_PLAYBACK_3)).isFalse();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_BACKUP_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_BACKUP_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_BACKUP_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_BACKUP_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_BACKUP_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_BACKUP_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_BACKUP_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_BACKUP_1)).isTrue();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_BACKUP_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_BACKUP_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_BACKUP_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_BACKUP_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_BACKUP_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_BACKUP_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_BACKUP_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_BACKUP_2)).isTrue();
+
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TV,
+ Constants.ADDR_SPECIFIC_USE)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RECORDER,
+ Constants.ADDR_SPECIFIC_USE)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_RESERVED,
+ Constants.ADDR_SPECIFIC_USE)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_TUNER,
+ Constants.ADDR_SPECIFIC_USE)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.ADDR_SPECIFIC_USE)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ Constants.ADDR_SPECIFIC_USE)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ Constants.ADDR_SPECIFIC_USE)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR,
+ Constants.ADDR_SPECIFIC_USE)).isFalse();
+ }
+
+ @Test
+ public void isEligibleAddressForCecVersion_1_4() {
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_TV)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_RECORDER_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_RECORDER_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_TUNER_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_PLAYBACK_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_AUDIO_SYSTEM)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_TUNER_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_TUNER_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_PLAYBACK_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_RECORDER_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_TUNER_4)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_PLAYBACK_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_BACKUP_1)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_BACKUP_2)).isFalse();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_1_4,
+ Constants.ADDR_SPECIFIC_USE)).isTrue();
+ }
+
+ @Test
+ public void isEligibleAddressForCecVersion_2_0() {
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_TV)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_RECORDER_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_RECORDER_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_TUNER_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_PLAYBACK_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_AUDIO_SYSTEM)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_TUNER_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_TUNER_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_PLAYBACK_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_RECORDER_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_TUNER_4)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_PLAYBACK_3)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_BACKUP_1)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_BACKUP_2)).isTrue();
+ assertThat(HdmiUtils.isEligibleAddressForCecVersion(Constants.VERSION_2_0,
+ Constants.ADDR_SPECIFIC_USE)).isTrue();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6255630712ae..95c881e1d927 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -63,7 +63,6 @@ import com.android.server.LocalServices;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
-import com.android.server.pm.permission.PermissionSettings;
import com.google.common.truth.Truth;
@@ -92,8 +91,6 @@ public class PackageManagerSettingsTests {
private static final int TEST_RESOURCE_ID = 2131231283;
@Mock
- PermissionSettings mPermissionSettings;
- @Mock
RuntimePermissionsPersistence mRuntimePermissionsPersistence;
@Mock
LegacyPermissionDataProvider mPermissionDataProvider;
@@ -117,7 +114,7 @@ public class PackageManagerSettingsTests {
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
+ Settings settings = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
verifyKeySetMetaData(settings);
@@ -131,7 +128,7 @@ public class PackageManagerSettingsTests {
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
+ Settings settings = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -147,7 +144,7 @@ public class PackageManagerSettingsTests {
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
+ Settings settings = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
@@ -169,13 +166,13 @@ public class PackageManagerSettingsTests {
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
+ Settings settings = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
settings.writeLPr();
// Create Settings again to make it read from the new files
- settings = new Settings(context.getFilesDir(), mPermissionSettings,
+ settings = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -199,7 +196,7 @@ public class PackageManagerSettingsTests {
writePackageRestrictions_noSuspendingPackageXml(0);
final Object lock = new Object();
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
lock);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
@@ -223,7 +220,7 @@ public class PackageManagerSettingsTests {
writePackageRestrictions_noSuspendParamsMapXml(0);
final Object lock = new Object();
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
lock);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
settingsUnderTest.readPackageRestrictionsLPr(0);
@@ -251,7 +248,7 @@ public class PackageManagerSettingsTests {
@Test
public void testReadWritePackageRestrictions_suspendInfo() {
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
new Object());
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
@@ -349,7 +346,7 @@ public class PackageManagerSettingsTests {
@Test
public void testReadWritePackageRestrictions_distractionFlags() {
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
new Object());
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
@@ -393,7 +390,7 @@ public class PackageManagerSettingsTests {
public void testWriteReadUsesStaticLibraries() {
final Context context = InstrumentationRegistry.getTargetContext();
final Object lock = new Object();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), mPermissionSettings,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
ps1.appId = Process.FIRST_APPLICATION_UID;
@@ -469,7 +466,7 @@ public class PackageManagerSettingsTests {
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
+ Settings settings = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -642,7 +639,7 @@ public class PackageManagerSettingsTests {
public void testUpdatePackageSetting03() {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
- final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings,
+ final Settings testSettings01 = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
@@ -752,7 +749,7 @@ public class PackageManagerSettingsTests {
public void testCreateNewSetting03() {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
- final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings,
+ final Settings testSettings01 = new Settings(context.getFilesDir(),
mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 0c6d6388b458..c43050386dcf 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -45,6 +45,11 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static com.android.server.usage.AppStandbyController.DEFAULT_ELAPSED_TIME_THRESHOLDS;
+import static com.android.server.usage.AppStandbyController.DEFAULT_SCREEN_TIME_THRESHOLDS;
+import static com.android.server.usage.AppStandbyController.MINIMUM_ELAPSED_TIME_THRESHOLDS;
+import static com.android.server.usage.AppStandbyController.MINIMUM_SCREEN_TIME_THRESHOLDS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -73,6 +78,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.view.Display;
@@ -184,6 +190,19 @@ public class AppStandbyControllerTests {
int[] mRunningUsers = new int[] {USER_ID};
List<UserHandle> mCrossProfileTargets = Collections.emptyList();
boolean mDeviceIdleMode = false;
+ DeviceConfig.Properties.Builder mSettingsBuilder =
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_APP_STANDBY)
+ .setLong("screen_threshold_active", 0)
+ .setLong("screen_threshold_working_set", 0)
+ .setLong("screen_threshold_frequent", 0)
+ .setLong("screen_threshold_rare", HOUR_MS)
+ // screen_threshold_restricted intentionally skipped
+ .setLong("elapsed_threshold_active", 0)
+ .setLong("elapsed_threshold_working_set", WORKING_SET_THRESHOLD)
+ .setLong("elapsed_threshold_frequent", FREQUENT_THRESHOLD)
+ .setLong("elapsed_threshold_rare", RARE_THRESHOLD)
+ .setLong("elapsed_threshold_restricted", RESTRICTED_THRESHOLD);
+ DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener;
MyInjector(Context context, Looper looper) {
super(context, looper);
@@ -285,10 +304,9 @@ public class AppStandbyControllerTests {
}
@Override
- String getAppIdleSettings() {
- return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
- + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD
- + "/" + RESTRICTED_THRESHOLD;
+ @NonNull
+ DeviceConfig.Properties getDeviceConfigProperties(String... keys) {
+ return mSettingsBuilder.build();
}
@Override
@@ -301,6 +319,12 @@ public class AppStandbyControllerTests {
return mCrossProfileTargets;
}
+ @Override
+ public void registerDeviceConfigPropertiesChangedListener(
+ @NonNull DeviceConfig.OnPropertiesChangedListener listener) {
+ mPropertiesChangedListener = listener;
+ }
+
// Internal methods
void setDisplayOn(boolean on) {
@@ -867,6 +891,25 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_RARE);
}
+ /** Test that timeouts still work properly even if invalid configuration values are set. */
+ @Test
+ public void testTimeout_InvalidThresholds() throws Exception {
+ mInjector.mSettingsBuilder
+ .setLong("screen_threshold_active", -1)
+ .setLong("screen_threshold_working_set", -1)
+ .setLong("screen_threshold_frequent", -1)
+ .setLong("screen_threshold_rare", -1)
+ .setLong("screen_threshold_restricted", -1)
+ .setLong("elapsed_threshold_active", -1)
+ .setLong("elapsed_threshold_working_set", -1)
+ .setLong("elapsed_threshold_frequent", -1)
+ .setLong("elapsed_threshold_rare", -1)
+ .setLong("elapsed_threshold_restricted", -1);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ testTimeout();
+ }
+
/**
* Test that setAppStandbyBucket to RESTRICTED doesn't change the bucket until the usage
* timeout has passed.
@@ -1554,6 +1597,132 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
+ @Test
+ public void testChangingSettings_ElapsedThreshold_Invalid() {
+ mInjector.mSettingsBuilder
+ .setLong("elapsed_threshold_active", -1)
+ .setLong("elapsed_threshold_working_set", -1)
+ .setLong("elapsed_threshold_frequent", -1)
+ .setLong("elapsed_threshold_rare", -1)
+ .setLong("elapsed_threshold_restricted", -1);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ for (int i = 0; i < MINIMUM_ELAPSED_TIME_THRESHOLDS.length; ++i) {
+ assertEquals(MINIMUM_ELAPSED_TIME_THRESHOLDS[i],
+ mController.mAppStandbyElapsedThresholds[i]);
+ }
+ }
+
+ @Test
+ public void testChangingSettings_ElapsedThreshold_Valid() {
+ // Effectively clear values
+ mInjector.mSettingsBuilder
+ .setString("elapsed_threshold_active", null)
+ .setString("elapsed_threshold_working_set", null)
+ .setString("elapsed_threshold_frequent", null)
+ .setString("elapsed_threshold_rare", null)
+ .setString("elapsed_threshold_restricted", null);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ for (int i = 0; i < DEFAULT_ELAPSED_TIME_THRESHOLDS.length; ++i) {
+ assertEquals(DEFAULT_ELAPSED_TIME_THRESHOLDS[i],
+ mController.mAppStandbyElapsedThresholds[i]);
+ }
+
+ // Set really high thresholds
+ mInjector.mSettingsBuilder
+ .setLong("elapsed_threshold_active", 90 * DAY_MS)
+ .setLong("elapsed_threshold_working_set", 91 * DAY_MS)
+ .setLong("elapsed_threshold_frequent", 92 * DAY_MS)
+ .setLong("elapsed_threshold_rare", 93 * DAY_MS)
+ .setLong("elapsed_threshold_restricted", 94 * DAY_MS);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ for (int i = 0; i < mController.mAppStandbyElapsedThresholds.length; ++i) {
+ assertEquals((90 + i) * DAY_MS, mController.mAppStandbyElapsedThresholds[i]);
+ }
+
+ // Only set a few values
+ mInjector.mSettingsBuilder
+ .setString("elapsed_threshold_active", null)
+ .setLong("elapsed_threshold_working_set", 31 * DAY_MS)
+ .setLong("elapsed_threshold_frequent", 62 * DAY_MS)
+ .setString("elapsed_threshold_rare", null)
+ .setLong("elapsed_threshold_restricted", 93 * DAY_MS);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+
+ assertEquals(DEFAULT_ELAPSED_TIME_THRESHOLDS[0],
+ mController.mAppStandbyElapsedThresholds[0]);
+ assertEquals(31 * DAY_MS, mController.mAppStandbyElapsedThresholds[1]);
+ assertEquals(62 * DAY_MS, mController.mAppStandbyElapsedThresholds[2]);
+ assertEquals(DEFAULT_ELAPSED_TIME_THRESHOLDS[3],
+ mController.mAppStandbyElapsedThresholds[3]);
+ assertEquals(93 * DAY_MS, mController.mAppStandbyElapsedThresholds[4]);
+ }
+
+ @Test
+ public void testChangingSettings_ScreenThreshold_Invalid() {
+ mInjector.mSettingsBuilder
+ .setLong("screen_threshold_active", -1)
+ .setLong("screen_threshold_working_set", -1)
+ .setLong("screen_threshold_frequent", -1)
+ .setLong("screen_threshold_rare", -1)
+ .setLong("screen_threshold_restricted", -1);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ for (int i = 0; i < MINIMUM_SCREEN_TIME_THRESHOLDS.length; ++i) {
+ assertEquals(MINIMUM_SCREEN_TIME_THRESHOLDS[i],
+ mController.mAppStandbyScreenThresholds[i]);
+ }
+ }
+
+ @Test
+ public void testChangingSettings_ScreenThreshold_Valid() {
+ // Effectively clear values
+ mInjector.mSettingsBuilder
+ .setString("screen_threshold_active", null)
+ .setString("screen_threshold_working_set", null)
+ .setString("screen_threshold_frequent", null)
+ .setString("screen_threshold_rare", null)
+ .setString("screen_threshold_restricted", null);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ for (int i = 0; i < DEFAULT_SCREEN_TIME_THRESHOLDS.length; ++i) {
+ assertEquals(DEFAULT_SCREEN_TIME_THRESHOLDS[i],
+ mController.mAppStandbyScreenThresholds[i]);
+ }
+
+ // Set really high thresholds
+ mInjector.mSettingsBuilder
+ .setLong("screen_threshold_active", 90 * DAY_MS)
+ .setLong("screen_threshold_working_set", 91 * DAY_MS)
+ .setLong("screen_threshold_frequent", 92 * DAY_MS)
+ .setLong("screen_threshold_rare", 93 * DAY_MS)
+ .setLong("screen_threshold_restricted", 94 * DAY_MS);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ for (int i = 0; i < mController.mAppStandbyScreenThresholds.length; ++i) {
+ assertEquals((90 + i) * DAY_MS, mController.mAppStandbyScreenThresholds[i]);
+ }
+
+ // Only set a few values
+ mInjector.mSettingsBuilder
+ .setString("screen_threshold_active", null)
+ .setLong("screen_threshold_working_set", 31 * DAY_MS)
+ .setLong("screen_threshold_frequent", 62 * DAY_MS)
+ .setString("screen_threshold_rare", null)
+ .setLong("screen_threshold_restricted", 93 * DAY_MS);
+ mInjector.mPropertiesChangedListener
+ .onPropertiesChanged(mInjector.getDeviceConfigProperties());
+
+ assertEquals(DEFAULT_SCREEN_TIME_THRESHOLDS[0], mController.mAppStandbyScreenThresholds[0]);
+ assertEquals(31 * DAY_MS, mController.mAppStandbyScreenThresholds[1]);
+ assertEquals(62 * DAY_MS, mController.mAppStandbyScreenThresholds[2]);
+ assertEquals(DEFAULT_SCREEN_TIME_THRESHOLDS[3], mController.mAppStandbyScreenThresholds[3]);
+ assertEquals(93 * DAY_MS, mController.mAppStandbyScreenThresholds[4]);
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index e304083dfd27..c50792866582 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -126,6 +126,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
users.add(mTen);
users.add(new UserInfo(11, "11", 0));
users.add(new UserInfo(12, "12", 0));
+ users.add(new UserInfo(13, "13", 0));
for (UserInfo user : users) {
when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
}
@@ -135,6 +136,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
profileIds.add(11);
profileIds.add(10);
profileIds.add(12);
+ profileIds.add(13);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
mVersionString = "2";
@@ -145,11 +147,13 @@ public class ManagedServicesTest extends UiServiceTestCase {
mExpectedPrimaryPackages.put(10, "this.is.another.package");
mExpectedPrimaryPackages.put(11, "");
mExpectedPrimaryPackages.put(12, "bananas!");
+ mExpectedPrimaryPackages.put(13, "non.user.set.package");
mExpectedPrimaryComponentNames = new ArrayMap<>();
mExpectedPrimaryComponentNames.put(0, "this.is.a.package.name/Ba:another.package/B1");
mExpectedPrimaryComponentNames.put(10, "this.is.another.package/M1");
mExpectedPrimaryComponentNames.put(11, "");
mExpectedPrimaryComponentNames.put(12, "bananas!/Bananas!");
+ mExpectedPrimaryComponentNames.put(13, "non.user.set.package/M1");
mExpectedPrimary.put(APPROVAL_BY_PACKAGE, mExpectedPrimaryPackages);
mExpectedPrimary.put(APPROVAL_BY_COMPONENT, mExpectedPrimaryComponentNames);
@@ -341,6 +345,35 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
}
+ /** Test that restore correctly parses the user_set attribute. */
+ @Test
+ public void testReadXml_restoresUserSet() throws Exception {
+ for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+ ManagedServices service =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, approvalLevel);
+ String testPackage = "user.test.package";
+ String testComponent = "user.test.component/C1";
+ String resolvedValue =
+ (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage;
+ String xmlEntry = getXmlEntry(resolvedValue, 0, true, false);
+ XmlPullParser parser = getParserWithEntries(service, xmlEntry);
+
+ service.readXml(parser, null, true, 0);
+
+ assertFalse("Failed while parsing xml:\n" + xmlEntry,
+ service.isPackageOrComponentUserSet(resolvedValue, 0));
+
+ xmlEntry = getXmlEntry(resolvedValue, 0, true, true);
+ parser = getParserWithEntries(service, xmlEntry);
+
+ service.readXml(parser, null, true, 0);
+
+ assertTrue("Failed while parsing xml:\n" + xmlEntry,
+ service.isPackageOrComponentUserSet(resolvedValue, 0));
+ }
+ }
+
/** Test that restore ignores the user id attribute and applies the data to the target user. */
@Test
public void testWriteReadXml_writeReadDefaults() throws Exception {
@@ -374,7 +407,6 @@ public class ManagedServicesTest extends UiServiceTestCase {
assertEquals(1, defaults.size());
assertEquals(new ComponentName("package", "class"), defaults.valueAt(0));
-
}
@Test
@@ -628,6 +660,47 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ public void testWriteXml_writesUserSet() throws Exception {
+ for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, approvalLevel);
+ loadXml(service);
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ service.writeXml(serializer, false, UserHandle.USER_ALL);
+ serializer.endDocument();
+ serializer.flush();
+
+ XmlPullParser parser = Xml.newPullParser();
+ byte[] rawOutput = baos.toByteArray();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(rawOutput)), null);
+ parser.nextTag();
+ for (UserInfo userInfo : mUm.getUsers()) {
+ service.readXml(parser, null, true, userInfo.id);
+ }
+
+ String resolvedUserSetComponent = approvalLevel == APPROVAL_BY_PACKAGE
+ ? mExpectedPrimaryPackages.get(10)
+ : mExpectedPrimaryComponentNames.get(10);
+ String resolvedNonUserSetComponent = approvalLevel == APPROVAL_BY_PACKAGE
+ ? mExpectedPrimaryPackages.get(13)
+ : mExpectedPrimaryComponentNames.get(13);
+
+ try {
+ assertFalse(service.isPackageOrComponentUserSet(resolvedNonUserSetComponent, 13));
+ assertTrue(service.isPackageOrComponentUserSet(resolvedUserSetComponent, 10));
+ } catch (AssertionError e) {
+ throw new AssertionError(
+ "Assertion failed while parsing xml:\n" + new String(rawOutput), e);
+ }
+ }
+ }
+
+ @Test
public void rebindServices_onlyBindsExactMatchesIfComponent() throws Exception {
// If the primary and secondary lists contain component names, only those components within
// the package should be matched
@@ -965,6 +1038,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
allowedPackages.add("package");
allowedPackages.add("component");
allowedPackages.add("bananas!");
+ allowedPackages.add("non.user.set.package");
Set<String> actual = service.getAllowedPackages();
assertEquals(allowedPackages.size(), actual.size());
@@ -1037,6 +1111,9 @@ public class ManagedServicesTest extends UiServiceTestCase {
expected12.add(ComponentName.unflattenFromString("bananas!/Bananas!"));
expected.put(12, expected12);
expected.put(11, new ArraySet<>());
+ ArraySet<ComponentName> expected13 = new ArraySet<>();
+ expected13.add(ComponentName.unflattenFromString("non.user.set.package/M1"));
+ expected.put(13, expected13);
SparseArray<ArraySet<ComponentName>> actual =
service.getAllowedComponents(mUserProfiles.getCurrentProfileIds());
@@ -1309,6 +1386,15 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
private void loadXml(ManagedServices service) throws Exception {
+ String xmlString = createXml(service);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xmlString.getBytes())), null);
+ parser.nextTag();
+ service.readXml(parser, null, false, UserHandle.USER_ALL);
+ }
+
+ private String createXml(ManagedServices service) {
final StringBuffer xml = new StringBuffer();
String xmlTag = service.getConfig().xmlTag;
xml.append("<" + xmlTag
@@ -1316,8 +1402,9 @@ public class ManagedServicesTest extends UiServiceTestCase {
+ (mVersionString != null ? " version=\"" + mVersionString + "\" " : "")
+ ">\n");
for (int userId : mExpectedPrimary.get(service.mApprovalLevel).keySet()) {
+ String pkgOrCmp = mExpectedPrimary.get(service.mApprovalLevel).get(userId);
xml.append(getXmlEntry(
- mExpectedPrimary.get(service.mApprovalLevel).get(userId), userId, true));
+ pkgOrCmp, userId, true, !(pkgOrCmp.startsWith("non.user.set.package"))));
}
for (int userId : mExpectedSecondary.get(service.mApprovalLevel).keySet()) {
xml.append(getXmlEntry(
@@ -1333,11 +1420,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
+ ManagedServices.ATT_APPROVED_LIST + "=\"98\" />\n");
xml.append("</" + xmlTag + ">");
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(
- new ByteArrayInputStream(xml.toString().getBytes())), null);
- parser.nextTag();
- service.readXml(parser, null, false, UserHandle.USER_ALL);
+ return xml.toString();
}
private XmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries)
@@ -1524,10 +1607,15 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
private String getXmlEntry(String approved, int userId, boolean isPrimary) {
+ return getXmlEntry(approved, userId, isPrimary, true);
+ }
+
+ private String getXmlEntry(String approved, int userId, boolean isPrimary, boolean userSet) {
return "<" + ManagedServices.TAG_MANAGED_SERVICES + " "
+ ManagedServices.ATT_USER_ID + "=\"" + userId +"\" "
+ ManagedServices.ATT_IS_PRIMARY + "=\"" + isPrimary +"\" "
+ ManagedServices.ATT_APPROVED_LIST + "=\"" + approved +"\" "
+ + ManagedServices.ATT_USER_SET + "=\"" + (userSet ? approved : "") + "\" "
+ "/>\n";
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 5796e848ff6e..f649911b6bb9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -134,7 +134,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
verify(mNm, never()).setDefaultAssistantForUser(anyInt());
verify(mAssistants, times(1)).addApprovedList(
- new ComponentName("b", "b").flattenToString(),10, true);
+ new ComponentName("b", "b").flattenToString(), 10, true, null);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index b77f8c5eb8cd..fdc089ee2f7e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -120,7 +120,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertEquals(canBubble(i), ranking.canBubble());
assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive());
assertEquals(isConversation(i), ranking.isConversation());
- assertEquals(getShortcutInfo(i).getId(), ranking.getShortcutInfo().getId());
+ assertEquals(getShortcutInfo(i).getId(), ranking.getConversationShortcutInfo().getId());
assertEquals(getRankingAdjustment(i), ranking.getRankingAdjustment());
}
}
@@ -191,7 +191,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
tweak.canBubble(),
tweak.visuallyInterruptive(),
tweak.isConversation(),
- tweak.getShortcutInfo(),
+ tweak.getConversationShortcutInfo(),
tweak.getRankingAdjustment(),
tweak.isBubble()
);
@@ -440,7 +440,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
assertEquals(comment, a.canBubble(), b.canBubble());
assertEquals(comment, a.isConversation(), b.isConversation());
- assertEquals(comment, a.getShortcutInfo().getId(), b.getShortcutInfo().getId());
+ assertEquals(comment, a.getConversationShortcutInfo().getId(), b.getConversationShortcutInfo().getId());
assertActionsEqual(a.getSmartActions(), b.getSmartActions());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 740505e0d3d7..87aaba2b164a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -111,9 +111,9 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Context;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.IIntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -2880,16 +2880,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testSetListenerAccessForUser() throws Exception {
UserHandle user = UserHandle.of(10);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true);
+ mBinderService.setNotificationListenerAccessGrantedForUser(
+ c, user.getIdentifier(), true, true);
verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
verify(mListeners, times(1)).setPackageOrComponentEnabled(
- c.flattenToString(), user.getIdentifier(), true, true);
+ c.flattenToString(), user.getIdentifier(), true, true, true);
verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
- c.flattenToString(), user.getIdentifier(), false, true);
+ c.flattenToString(), user.getIdentifier(), false, true, true);
verify(mAssistants, never()).setPackageOrComponentEnabled(
- any(), anyInt(), anyBoolean(), anyBoolean());
+ any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
}
@Test
@@ -2958,12 +2959,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testSetListenerAccess() throws Exception {
ComponentName c = ComponentName.unflattenFromString("package/Component");
- mBinderService.setNotificationListenerAccessGranted(c, true);
+ mBinderService.setNotificationListenerAccessGranted(c, true, true);
verify(mListeners, times(1)).setPackageOrComponentEnabled(
- c.flattenToString(), 0, true, true);
+ c.flattenToString(), 0, true, true, true);
verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
- c.flattenToString(), 0, false, true);
+ c.flattenToString(), 0, false, true, true);
verify(mAssistants, never()).setPackageOrComponentEnabled(
any(), anyInt(), anyBoolean(), anyBoolean());
}
@@ -3112,12 +3113,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testSetListenerAccess_onLowRam() throws Exception {
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- mBinderService.setNotificationListenerAccessGranted(c, true);
+ mBinderService.setNotificationListenerAccessGranted(c, true, true);
verify(mListeners).setPackageOrComponentEnabled(
- anyString(), anyInt(), anyBoolean(), anyBoolean());
+ anyString(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
verify(mConditionProviders).setPackageOrComponentEnabled(
- anyString(), anyInt(), anyBoolean(), anyBoolean());
+ anyString(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
verify(mAssistants).migrateToXml();
verify(mAssistants).resetDefaultAssistantsIfNecessary();
}
@@ -3160,14 +3161,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mActivityManager.isLowRamDevice()).thenReturn(true);
ComponentName c = ComponentName.unflattenFromString("package/Component");
- mBinderService.setNotificationListenerAccessGranted(c, true);
+ mBinderService.setNotificationListenerAccessGranted(c, true, true);
verify(mListeners, times(1)).setPackageOrComponentEnabled(
- c.flattenToString(), 0, true, true);
+ c.flattenToString(), 0, true, true, true);
verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
- c.flattenToString(), 0, false, true);
+ c.flattenToString(), 0, false, true, true);
verify(mAssistants, never()).setPackageOrComponentEnabled(
- any(), anyInt(), anyBoolean(), anyBoolean());
+ any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 3e779a9b2435..bf742b7f96dc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -218,6 +218,22 @@ public class PreferencesHelperTest extends UiServiceTestCase {
return null;
}).when(mTestIContentProvider).canonicalizeAsync(any(), any(), any(), any());
doAnswer(invocation -> {
+ String callingPkg = invocation.getArgument(0);
+ String featureId = invocation.getArgument(1);
+ Uri uri = invocation.getArgument(2);
+ RemoteCallback cb = invocation.getArgument(3);
+ IContentProvider mock = (IContentProvider) (invocation.getMock());
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ try {
+ bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+ mock.uncanonicalize(callingPkg, featureId, uri));
+ } catch (RemoteException e) { /* consume */ }
+ cb.sendResult(bundle);
+ });
+ return null;
+ }).when(mTestIContentProvider).uncanonicalizeAsync(any(), any(), any(), any());
+ doAnswer(invocation -> {
Uri uri = invocation.getArgument(0);
RemoteCallback cb = invocation.getArgument(1);
IContentProvider mock = (IContentProvider) (invocation.getMock());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
index 54b2b3b4a009..3220d1d6a990 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
@@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,6 +32,7 @@ import android.os.Binder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
+import android.window.DisplayAreaAppearedInfo;
import android.window.DisplayAreaInfo;
import android.window.IDisplayAreaOrganizer;
@@ -41,6 +43,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
/**
* Build/Install/Run:
* atest WmTests:DisplayAreaOrganizerTest
@@ -89,27 +93,39 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
}
@Test
+ public void testRegisterOrganizer() throws RemoteException {
+ IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ List<DisplayAreaAppearedInfo> infos = mWm.mAtmService.mWindowOrganizerController
+ .mDisplayAreaOrganizerController
+ .registerOrganizer(organizer, FEATURE_VENDOR_FIRST).getList();
+
+ // Return a list contains the DA, and no onDisplayAreaAppeared triggered.
+ assertThat(infos).hasSize(1);
+ assertThat(infos.get(0).getDisplayAreaInfo().token)
+ .isEqualTo(mTestDisplayArea.getDisplayAreaInfo().token);
+ verify(organizer, never()).onDisplayAreaAppeared(any(DisplayAreaInfo.class),
+ any(SurfaceControl.class));
+ }
+
+ @Test
public void testAppearedVanished() throws RemoteException {
IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST);
- verify(organizer)
- .onDisplayAreaAppeared(any(DisplayAreaInfo.class), any(SurfaceControl.class));
-
unregisterMockOrganizer(organizer);
+
verify(organizer).onDisplayAreaVanished(any());
}
@Test
public void testChanged() throws RemoteException {
IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST);
- verify(organizer)
- .onDisplayAreaAppeared(any(DisplayAreaInfo.class), any(SurfaceControl.class));
-
mDisplayContent.setBounds(new Rect(0, 0, 1000, 1000));
+
verify(organizer).onDisplayAreaInfoChanged(any());
Configuration tmpConfiguration = new Configuration();
tmpConfiguration.setTo(mDisplayContent.getRequestedOverrideConfiguration());
mDisplayContent.onRequestedOverrideConfigurationChanged(tmpConfiguration);
+
// Ensure it was still only called once if the bounds didn't change
verify(organizer).onDisplayAreaInfoChanged(any());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index c9e56fde0fb9..f52f98389872 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -36,6 +36,7 @@ import static android.view.Surface.ROTATION_90;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
@@ -1606,6 +1607,29 @@ public class DisplayContentTests extends WindowTestsBase {
verifySizes(dc, forcedWidth, forcedHeight, forcedDensity);
}
+ @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
+ @Test
+ public void testComputeImeTarget_shouldNotCheckOutdatedImeTargetLayerWhenRemoved() {
+ final WindowState child1 = createWindow(mAppWindow, FIRST_SUB_WINDOW, "child1");
+ final WindowState nextImeTargetApp = createWindow(null /* parent */,
+ TYPE_BASE_APPLICATION, "nextImeTargetApp");
+ spyOn(child1);
+ doReturn(true).when(child1).inSplitScreenWindowingMode();
+ mDisplayContent.mInputMethodTarget = child1;
+
+ spyOn(nextImeTargetApp);
+ spyOn(mAppWindow);
+ doReturn(true).when(nextImeTargetApp).canBeImeTarget();
+ doReturn(true).when(nextImeTargetApp).isActivityTypeHome();
+ doReturn(false).when(mAppWindow).canBeImeTarget();
+
+ child1.removeImmediately();
+
+ verify(mDisplayContent).computeImeTarget(true);
+ assertNull(mDisplayContent.mInputMethodInputTarget);
+ verify(child1, never()).needsRelativeLayeringToIme();
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 3ff859b55bdd..51e1ebc63cf2 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -158,7 +158,8 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P
*
* Asu is calculated based on 3GPP RSSI. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
*
- * @return RSSI in ASU 0..31, 99, or UNAVAILABLE
+ * @return RSSI in ASU 0..31, 99, or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
*/
@Override
public int getAsuLevel() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 19e70c8d1443..c982f490e0fd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10571,6 +10571,9 @@ public class TelephonyManager {
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
+ * If you want continuous updates of service state info, register a {@link PhoneStateListener}
+ * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event.
+ *
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
* and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 41381c59482b..3e2a6eec37c4 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -109,8 +109,14 @@ public final class DataCallResponse implements Parcelable {
*/
public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3;
+ /**
+ * Indicates that data retry interval is not specified. Platform can determine when to
+ * perform data setup appropriately.
+ */
+ public static final int RETRY_INTERVAL_UNDEFINED = -1;
+
private final @DataFailureCause int mCause;
- private final int mSuggestedRetryTime;
+ private final long mSuggestedRetryTime;
private final int mId;
private final @LinkStatus int mLinkStatus;
private final @ProtocolType int mProtocolType;
@@ -172,7 +178,7 @@ public final class DataCallResponse implements Parcelable {
mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
}
- private DataCallResponse(@DataFailureCause int cause, int suggestedRetryTime, int id,
+ private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@LinkStatus int linkStatus, @ProtocolType int protocolType,
@Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
@@ -202,7 +208,7 @@ public final class DataCallResponse implements Parcelable {
@VisibleForTesting
public DataCallResponse(Parcel source) {
mCause = source.readInt();
- mSuggestedRetryTime = source.readInt();
+ mSuggestedRetryTime = source.readLong();
mId = source.readInt();
mLinkStatus = source.readInt();
mProtocolType = source.readInt();
@@ -229,8 +235,22 @@ public final class DataCallResponse implements Parcelable {
/**
* @return The suggested data retry time in milliseconds.
+ *
+ * @deprecated Use {@link #getRetryIntervalMillis()} instead.
+ */
+ @Deprecated
+ public int getSuggestedRetryTime() {
+ return (int) mSuggestedRetryTime;
+ }
+
+ /**
+ * @return The network suggested data retry interval in milliseconds. {@code Long.MAX_VALUE}
+ * indicates data retry should not occur. {@link #RETRY_INTERVAL_UNDEFINED} indicates network
+ * did not suggest any retry interval.
*/
- public int getSuggestedRetryTime() { return mSuggestedRetryTime; }
+ public long getRetryIntervalMillis() {
+ return mSuggestedRetryTime;
+ }
/**
* @return The unique id of the data connection.
@@ -382,7 +402,7 @@ public final class DataCallResponse implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mCause);
- dest.writeInt(mSuggestedRetryTime);
+ dest.writeLong(mSuggestedRetryTime);
dest.writeInt(mId);
dest.writeInt(mLinkStatus);
dest.writeInt(mProtocolType);
@@ -446,7 +466,7 @@ public final class DataCallResponse implements Parcelable {
public static final class Builder {
private @DataFailureCause int mCause;
- private int mSuggestedRetryTime;
+ private long mSuggestedRetryTime = RETRY_INTERVAL_UNDEFINED;
private int mId;
@@ -494,9 +514,23 @@ public final class DataCallResponse implements Parcelable {
*
* @param suggestedRetryTime The suggested data retry time in milliseconds.
* @return The same instance of the builder.
+ *
+ * @deprecated Use {@link #setRetryIntervalMillis(long)} instead.
*/
+ @Deprecated
public @NonNull Builder setSuggestedRetryTime(int suggestedRetryTime) {
- mSuggestedRetryTime = suggestedRetryTime;
+ mSuggestedRetryTime = (long) suggestedRetryTime;
+ return this;
+ }
+
+ /**
+ * Set the network suggested data retry interval.
+ *
+ * @param retryIntervalMillis The suggested data retry interval in milliseconds.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setRetryIntervalMillis(long retryIntervalMillis) {
+ mSuggestedRetryTime = retryIntervalMillis;
return this;
}
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index a5c254f1aca2..5b9f67efd95d 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -169,6 +169,12 @@ public class MockContentProvider extends ContentProvider {
}
@Override
+ public void uncanonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ RemoteCallback callback) {
+ MockContentProvider.this.uncanonicalizeAsync(uri, callback);
+ }
+
+ @Override
public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
Bundle args, ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.refresh(url, args);
@@ -311,6 +317,18 @@ public class MockContentProvider extends ContentProvider {
/**
* @hide
*/
+ @SuppressWarnings("deprecation")
+ public void uncanonicalizeAsync(Uri uri, RemoteCallback callback) {
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, uncanonicalize(uri));
+ callback.sendResult(bundle);
+ });
+ }
+
+ /**
+ * @hide
+ */
public boolean refresh(Uri url, Bundle args) {
throw new UnsupportedOperationException("unimplemented mock method call");
}
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 223bcc59039d..82a1cf7d1796 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -162,12 +162,23 @@ public class MockIContentProvider implements IContentProvider {
}
@Override
- public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
- throws RemoteException {
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
+ @SuppressWarnings("deprecation")
+ public void uncanonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ RemoteCallback remoteCallback) {
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+ uncanonicalize(callingPkg, featureId, uri));
+ remoteCallback.sendResult(bundle);
+ });
+ }
+
+ @Override
public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index 02f5286506a8..603c87519532 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -19,6 +19,8 @@ package android.net.ipmemorystore;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk;
+import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirkParcelable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -46,7 +48,7 @@ public class ParcelableTests {
builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
// lease will expire in two hours
builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000);
- // groupHint stays null this time around
+ // cluster stays null this time around
builder.setDnsAddresses(Collections.emptyList());
builder.setMtu(18);
in = builder.build();
@@ -69,7 +71,7 @@ public class ParcelableTests {
// Verify that this test does not miss any new field added later.
// If any field is added to NetworkAttributes it must be tested here for parceling
// roundtrip.
- assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+ assertEquals(6, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
.filter(f -> !Modifier.isStatic(f.getModifiers())).count());
}
@@ -104,6 +106,22 @@ public class ParcelableTests {
assertEquals(in.confidence, out.confidence, 0.01f /* delta */);
}
+ @Test
+ public void testIPv6ProvisioningLossQuirkParceling() throws Exception {
+ final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
+ final IPv6ProvisioningLossQuirkParcelable parcelable =
+ new IPv6ProvisioningLossQuirkParcelable();
+ final long expiry = System.currentTimeMillis() + 7_200_000;
+
+ parcelable.detectionCount = 3;
+ parcelable.quirkExpiry = expiry; // quirk info will expire in two hours
+ builder.setIpv6ProvLossQuirk(IPv6ProvisioningLossQuirk.fromStableParcelable(parcelable));
+ final NetworkAttributes in = builder.build();
+
+ final NetworkAttributes out = new NetworkAttributes(parcelingRoundTrip(in.toParcelable()));
+ assertEquals(out.ipv6ProvisioningLossQuirk, in.ipv6ProvisioningLossQuirk);
+ }
+
private <T extends Parcelable> T parcelingRoundTrip(final T in) throws Exception {
final Parcel p = Parcel.obtain();
in.writeToParcel(p, /* flags */ 0);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 4081346f62f9..2a0c99c3bc52 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -6023,23 +6023,23 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mNetworkManagementService);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
- verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId));
- reset(mNetworkManagementService);
+ verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
- verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mNetworkManagementService);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- verify(mNetworkManagementService).clearDefaultNetId();
+ verify(mMockNetd).networkClearDefault();
mCm.unregisterNetworkCallback(trustedCallback);
}
@@ -6143,6 +6143,7 @@ public class ConnectivityServiceTest {
verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt());
assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
+ verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
TYPE_MOBILE);
@@ -6199,7 +6200,7 @@ public class ConnectivityServiceTest {
.getStackedLinks();
assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
assertRoutesAdded(cellNetId, stackedDefault);
-
+ verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
// Change trivial linkproperties and see if stacked link is preserved.
cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
mCellNetworkAgent.sendLinkProperties(cellLp);
@@ -6230,6 +6231,7 @@ public class ConnectivityServiceTest {
(lp) -> lp.getStackedLinks().size() == 0);
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
assertRoutesRemoved(cellNetId, stackedDefault);
+ verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString());
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
@@ -6238,6 +6240,7 @@ public class ConnectivityServiceTest {
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 1);
assertRoutesAdded(cellNetId, stackedDefault);
+ verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
reset(mMockNetd);
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
@@ -6262,7 +6265,7 @@ public class ConnectivityServiceTest {
// The interface removed callback happens but has no effect after stop is called.
clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
networkCallback.assertNoCallback();
-
+ verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
reset(mNetworkManagementService);
@@ -6295,6 +6298,7 @@ public class ConnectivityServiceTest {
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null);
assertRoutesAdded(cellNetId, stackedDefault);
+ verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
// NAT64 prefix is removed. Expect that clat is stopped.
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
@@ -6307,8 +6311,8 @@ public class ConnectivityServiceTest {
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 0);
+ verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
verifyNoMoreInteractions(mMockNetd);
-
// Clean up.
mCellNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
index fb84611cb662..ebbc0ef62548 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -19,6 +19,7 @@ package com.android.server.net.ipmemorystore;
import static org.junit.Assert.assertEquals;
import android.net.ipmemorystore.NetworkAttributes;
+import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -52,6 +53,8 @@ public class NetworkAttributesTest {
}
assertEquals(sum, NetworkAttributes.TOTAL_WEIGHT, EPSILON);
+ final IPv6ProvisioningLossQuirk ipv6ProvisioningLossQuirk =
+ new IPv6ProvisioningLossQuirk(3, System.currentTimeMillis() + 7_200_000);
// Use directly the constructor with all attributes, and make sure that when compared
// to itself the score is a clean 1.0f.
final NetworkAttributes na =
@@ -61,7 +64,7 @@ public class NetworkAttributesTest {
"some hint",
Arrays.asList(Inet4Address.getByAddress(new byte[] {5, 6, 7, 8}),
Inet4Address.getByAddress(new byte[] {9, 0, 1, 2})),
- 98);
+ 98, ipv6ProvisioningLossQuirk);
assertEquals(1.0f, na.getNetworkGroupSamenessConfidence(na), EPSILON);
}
}
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 61ce44c3b5b9..2759e29d1620 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -16,6 +16,7 @@ cc_binary_host {
static_libs: [
"libbase",
+ "libbinder",
"libinput",
"libutils",
"libcutils",
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 3040041038cb..52e3840c126e 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -75,25 +75,34 @@ test_access_hidden_api_whitelist = [
"//external/sl4a:__subpackages__",
]
-// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility
-// classes before they are renamed.
-java_library {
- name: "framework-wifi-pre-jarjar",
+// defaults shared between `framework-wifi` & `framework-wifi-pre-jarjar`
+// java_sdk_library `framework-wifi` needs sources to generate stubs, so it cannot reuse
+// `framework-wifi-pre-jarjar`
+java_defaults {
+ name: "framework-wifi-defaults",
defaults: ["wifi-module-sdk-version-defaults"],
- sdk_version: "module_current",
static_libs: [
"framework-wifi-util-lib",
"android.hardware.wifi-V1.0-java-constants",
+ "modules-utils-build",
],
libs: [
- "framework-annotations-lib",
"unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
],
srcs: [
":framework-wifi-updatable-sources",
":framework-wifi-util-lib-aidls",
],
- // java_api_finder must accompany `srcs`
+}
+
+// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility
+// classes before they are renamed.
+java_library {
+ name: "framework-wifi-pre-jarjar",
+ defaults: ["framework-wifi-defaults"],
+ sdk_version: "module_current",
+ libs: ["framework-annotations-lib"],
+ // java_api_finder must accompany `srcs` (`srcs` defined in `framework-wifi-defaults`)
plugins: ["java_api_finder"],
installable: false,
visibility: [
@@ -107,18 +116,7 @@ java_sdk_library {
name: "framework-wifi",
defaults: [
"framework-module-defaults",
- "wifi-module-sdk-version-defaults",
- ],
- static_libs: [
- "framework-wifi-util-lib",
- "android.hardware.wifi-V1.0-java-constants",
- ],
- libs: [
- "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
- ],
- srcs: [
- ":framework-wifi-updatable-sources",
- ":framework-wifi-util-lib-aidls",
+ "framework-wifi-defaults",
],
jarjar_rules: ":wifi-jarjar-rules",
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index 5dc5dfc02bce..7c592f0aec9f 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -336,8 +336,11 @@ package android.net.wifi {
field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
+ field @Deprecated public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004; // 0x3ec
field @Deprecated public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; // 0x3e9
field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
+ field @Deprecated public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003; // 0x3eb
+ field @Deprecated public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002; // 0x3ea
field @Deprecated public boolean allowAutojoin;
field @Deprecated public int carrierId;
field @Deprecated public String creatorName;
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index eef08b54f570..b0ff4bb27e70 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -36,6 +36,7 @@ rule android.net.ipmemorystore.IOnStatusListener* com.android.wifi.x.@0
rule android.net.ipmemorystore.NetworkAttributesParcelable* com.android.wifi.x.@0
rule android.net.ipmemorystore.SameL3NetworkResponseParcelable* com.android.wifi.x.@0
rule android.net.ipmemorystore.StatusParcelable* com.android.wifi.x.@0
+rule android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk* com.android.wifi.x.@0
# Net utils (includes Network Stack helper classes).
rule android.net.DhcpResults* com.android.wifi.x.@0
@@ -123,3 +124,4 @@ rule com.android.internal.util.Preconditions* com.android.wifi.x.@0
rule com.android.internal.util.Protocol* com.android.wifi.x.@0
rule com.android.net.module.util.** com.android.wifi.x.@0
+rule com.android.modules.utils.** com.android.wifi.x.@0
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index cf54f26fc5e4..6bd4211d8214 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -21,10 +21,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.wifi.SoftApConfiguration.BandType;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.modules.utils.build.SdkLevel;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@@ -176,7 +177,7 @@ public final class SoftApCapability implements Parcelable {
*/
@NonNull
public int[] getSupportedChannelList(@BandType int band) {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
switch (band) {
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 5cbbb0dcdedf..2c53daaf55a7 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -22,7 +22,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.MacAddress;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -31,6 +30,7 @@ import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.modules.utils.build.SdkLevel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -499,7 +499,7 @@ public final class SoftApConfiguration implements Parcelable {
*/
@SystemApi
public @NonNull int[] getBands() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
int[] bands = new int[mChannels.size()];
@@ -535,7 +535,7 @@ public final class SoftApConfiguration implements Parcelable {
*/
@SystemApi
public @NonNull SparseIntArray getChannels() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return mChannels;
@@ -635,7 +635,7 @@ public final class SoftApConfiguration implements Parcelable {
@SystemApi
@MacRandomizationSetting
public int getMacRandomizationSetting() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return mMacRandomizationSetting;
@@ -1219,7 +1219,7 @@ public final class SoftApConfiguration implements Parcelable {
@NonNull
public Builder setMacRandomizationSetting(
@MacRandomizationSetting int macRandomizationSetting) {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
mMacRandomizationSetting = macRandomizationSetting;
diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java
index cf61f81e6d22..9a16facfec26 100644
--- a/wifi/java/android/net/wifi/SoftApInfo.java
+++ b/wifi/java/android/net/wifi/SoftApInfo.java
@@ -20,11 +20,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.MacAddress;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
+import com.android.modules.utils.build.SdkLevel;
import java.util.Objects;
@@ -141,7 +141,7 @@ public final class SoftApInfo implements Parcelable {
*/
@Nullable
public MacAddress getBssid() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return mBssid;
@@ -180,7 +180,7 @@ public final class SoftApInfo implements Parcelable {
* @return valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
*/
public @WifiAnnotations.WifiStandard int getWifiStandard() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return mWifiStandard;
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 9a8a5ad4b41f..9ca64d2ae2ef 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2074,27 +2074,42 @@ public class WifiConfiguration implements Parcelable {
*/
@RecentFailureReason
private int mAssociationStatus = RECENT_FAILURE_NONE;
+ private long mLastUpdateTimeSinceBootMillis;
/**
* @param status the association status code for the recent failure
*/
- public void setAssociationStatus(@RecentFailureReason int status) {
+ public void setAssociationStatus(@RecentFailureReason int status,
+ long updateTimeSinceBootMs) {
mAssociationStatus = status;
+ mLastUpdateTimeSinceBootMillis = updateTimeSinceBootMs;
}
/**
* Sets the RecentFailure to NONE
*/
public void clear() {
mAssociationStatus = RECENT_FAILURE_NONE;
+ mLastUpdateTimeSinceBootMillis = 0;
}
/**
- * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE} or
- * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}.
+ * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE},
+ * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA},
+ * {@link #RECENT_FAILURE_MBO_OCE_DISCONNECT},
+ * {@link #RECENT_FAILURE_REFUSED_TEMPORARILY},
+ * {@link #RECENT_FAILURE_POOR_CHANNEL_CONDITIONS}.
+ * {@link #RECENT_FAILURE_DISCONNECTION_AP_BUSY}
*/
@RecentFailureReason
public int getAssociationStatus() {
return mAssociationStatus;
}
+
+ /**
+ * Get the timestamp the failure status is last updated, in milliseconds since boot.
+ */
+ public long getLastUpdateTimeSinceBootMillis() {
+ return mLastUpdateTimeSinceBootMillis;
+ }
}
/**
@@ -2111,7 +2126,11 @@ public class WifiConfiguration implements Parcelable {
@IntDef(prefix = "RECENT_FAILURE_", value = {
RECENT_FAILURE_NONE,
RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA,
- RECENT_FAILURE_MBO_OCE_DISCONNECT})
+ RECENT_FAILURE_MBO_OCE_DISCONNECT,
+ RECENT_FAILURE_REFUSED_TEMPORARILY,
+ RECENT_FAILURE_POOR_CHANNEL_CONDITIONS,
+ RECENT_FAILURE_DISCONNECTION_AP_BUSY
+ })
public @interface RecentFailureReason {}
/**
@@ -2136,12 +2155,39 @@ public class WifiConfiguration implements Parcelable {
public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001;
/**
+ * Failed to connect because the association is rejected by the AP.
+ * IEEE 802.11 association status code 30.
+ * @hide
+ */
+ @SystemApi
+ public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002;
+
+ /**
+ * Failed to connect because of excess frame loss and/or poor channel conditions.
+ * IEEE 802.11 association status code 34.
+ * @hide
+ */
+ @SystemApi
+ public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003;
+
+ /**
+ * Disconnected by the AP because the AP can't handle all the associated stations.
+ * IEEE 802.11 disconnection reason code 5.
+ * @hide
+ */
+ @SystemApi
+ public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004;
+
+ /**
* Get the failure reason for the most recent connection attempt, or
* {@link #RECENT_FAILURE_NONE} if there was no failure.
*
* Failure reasons include:
* {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}
- *
+ * {@link #RECENT_FAILURE_MBO_OCE_DISCONNECT}
+ * {@link #RECENT_FAILURE_REFUSED_TEMPORARILY}
+ * {@link #RECENT_FAILURE_POOR_CHANNEL_CONDITIONS}
+ * {@link #RECENT_FAILURE_DISCONNECTION_AP_BUSY}
* @hide
*/
@RecentFailureReason
@@ -2479,7 +2525,8 @@ public class WifiConfiguration implements Parcelable {
}
}
sbuf.append("recentFailure: ").append("Association Rejection code: ")
- .append(recentFailure.getAssociationStatus()).append("\n");
+ .append(recentFailure.getAssociationStatus()).append(", last update time: ")
+ .append(recentFailure.getLastUpdateTimeSinceBootMillis()).append("\n");
return sbuf.toString();
}
@@ -2918,7 +2965,8 @@ public class WifiConfiguration implements Parcelable {
numNoInternetAccessReports = source.numNoInternetAccessReports;
noInternetAccessExpected = source.noInternetAccessExpected;
shared = source.shared;
- recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus());
+ recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus(),
+ source.recentFailure.getLastUpdateTimeSinceBootMillis());
mRandomizedMacAddress = source.mRandomizedMacAddress;
macRandomizationSetting = source.macRandomizationSetting;
randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs;
@@ -2995,6 +3043,7 @@ public class WifiConfiguration implements Parcelable {
dest.writeInt(shared ? 1 : 0);
dest.writeString(mPasspointManagementObjectTree);
dest.writeInt(recentFailure.getAssociationStatus());
+ dest.writeLong(recentFailure.getLastUpdateTimeSinceBootMillis());
dest.writeParcelable(mRandomizedMacAddress, flags);
dest.writeInt(macRandomizationSetting);
dest.writeInt(osu ? 1 : 0);
@@ -3071,7 +3120,7 @@ public class WifiConfiguration implements Parcelable {
config.noInternetAccessExpected = in.readInt() != 0;
config.shared = in.readInt() != 0;
config.mPasspointManagementObjectTree = in.readString();
- config.recentFailure.setAssociationStatus(in.readInt());
+ config.recentFailure.setAssociationStatus(in.readInt(), in.readLong());
config.mRandomizedMacAddress = in.readParcelable(null);
config.macRandomizationSetting = in.readInt();
config.osu = in.readInt() != 0;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 78bf88b7ca78..8ee08f1610d6 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -44,7 +44,6 @@ import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.ProvisioningCallback;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -62,6 +61,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2493,7 +2493,7 @@ public class WifiManager {
* @return true if this device supports multiple STA concurrency, false otherwise.
*/
public boolean isMultiStaConcurrencySupported() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return isFeatureSupported(WIFI_FEATURE_ADDITIONAL_STA);
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 0b368708d283..acae218b74e0 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -26,7 +26,6 @@ import android.annotation.SystemApi;
import android.net.MacAddress;
import android.net.NetworkCapabilities;
import android.net.wifi.hotspot2.PasspointConfiguration;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.SubscriptionInfo;
@@ -34,6 +33,8 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import com.android.modules.utils.build.SdkLevel;
+
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
@@ -451,7 +452,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
* @return Instance of {@link Builder} to enable chaining of the builder method.
*/
public @NonNull Builder setSubscriptionId(int subId) {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
mSubscriptionId = subId;
@@ -466,7 +467,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
* @return Instance of {@link Builder} to enable chaining of the builder method.
*/
public @NonNull Builder setPriorityGroup(int priorityGroup) {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
mPriorityGroup = priorityGroup;
@@ -707,7 +708,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
*/
@SystemApi
public @NonNull Builder setOemPaid(boolean isOemPaid) {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
mIsNetworkOemPaid = isOemPaid;
@@ -1204,7 +1205,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
*/
@SystemApi
public boolean isOemPaid() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return wifiConfiguration.oemPaid;
@@ -1242,7 +1243,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
* @see Builder#setPriorityGroup(int)
*/
public int getPriorityGroup() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return priorityGroup;
@@ -1252,7 +1253,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
* @see Builder#setSubscriptionId(int)
*/
public int getSubscriptionId() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return wifiConfiguration.subscriptionId;
diff --git a/wifi/java/android/net/wifi/aware/Characteristics.java b/wifi/java/android/net/wifi/aware/Characteristics.java
index 116786516b3b..9bdda7f7d323 100644
--- a/wifi/java/android/net/wifi/aware/Characteristics.java
+++ b/wifi/java/android/net/wifi/aware/Characteristics.java
@@ -17,11 +17,12 @@
package android.net.wifi.aware;
import android.annotation.IntDef;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.modules.utils.build.SdkLevel;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -92,7 +93,7 @@ public final class Characteristics implements Parcelable {
* @return True if supported, false otherwise.
*/
public boolean isInstantCommunicationModeSupported() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
return mCharacteristics.getBoolean(KEY_IS_INSTANT_COMMUNICATION_MODE_SUPPORTED);
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index bb146e37d48c..67c60322e47a 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -29,7 +29,6 @@ import android.net.ConnectivityManager;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.wifi.util.HexEncoding;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -39,6 +38,8 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.build.SdkLevel;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -210,7 +211,7 @@ public class WifiAwareManager {
* or not (false).
*/
public boolean isDeviceAttached() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
try {
@@ -229,7 +230,7 @@ public class WifiAwareManager {
*/
@SystemApi
public void enableInstantCommunicationMode(boolean enable) {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
try {
@@ -246,7 +247,7 @@ public class WifiAwareManager {
* @return true if it is enabled, false otherwise.
*/
public boolean isInstantCommunicationModeEnabled() {
- if (!SdkLevelUtil.isAtLeastS()) {
+ if (!SdkLevel.isAtLeastS()) {
throw new UnsupportedOperationException();
}
try {
diff --git a/wifi/java/android/net/wifi/util/SdkLevelUtil.java b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
deleted file mode 100644
index d08d4fd742b7..000000000000
--- a/wifi/java/android/net/wifi/util/SdkLevelUtil.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.util;
-
-import android.os.Build;
-
-/**
- * Utility to check the SDK version of the device that the code is running on.
- *
- * This can be used to disable new Wifi APIs added in Mainline updates on older SDK versions.
- *
- * Note: if certain functionality is gated with SdkLevelUtil, its corresponding unit tests should
- * also be gated by the same condition. Then, those unit tests will only be exercised on a base
- * system image satisfying that condition.
- * Alternatively, it can be tested via static mocking.
- *
- * @hide
- */
-public class SdkLevelUtil {
-
- /** This class is not instantiable. */
- private SdkLevelUtil() {}
-
- /** Returns true if the Android platform SDK is at least "S", false otherwise. */
- public static boolean isAtLeastS() {
- // TODO(b/167575586): after S SDK finalization, this method should just be
- // `return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;`
-
- // at least S: return true
- // this condition only evaluates to true after S SDK finalization when VERSION_CODES.S
- // is set to something like "31", before SDK finalization the value is "10000"
- // Note that Build.VERSION_CODES.S is inlined at compile time. If it's inlined to 10000,
- // this condition never evaluates to true.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- return true;
- }
-
- // Assume for now that S = R + 1
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
- return true;
- }
-
- // R: check CODENAME
- // Before S SDK finalization, SDK_INT = R = 30 i.e. remains on the previous version
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
- // CODENAME = "REL" on R release builds
- // CODENAME = "S" on S development builds
- return "S".equals(Build.VERSION.CODENAME);
- }
-
- // older than R: return false
- return false;
- }
-}
diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp
index 6a39959e8cfd..b710a1492d8c 100644
--- a/wifi/tests/Android.bp
+++ b/wifi/tests/Android.bp
@@ -31,10 +31,11 @@ android_test {
static_libs: [
"androidx.test.rules",
"core-test-rules",
+ "frameworks-base-testutils",
"guava",
"mockito-target-minus-junit4",
+ "modules-utils-build",
"net-tests-utils",
- "frameworks-base-testutils",
"truth-prebuilt",
],
@@ -47,4 +48,8 @@ android_test {
"device-tests",
"mts",
],
+
+ // static libs used by both framework-wifi & FrameworksWifiApiTests. Need to rename test usage
+ // to a different package name to prevent conflict with the copy in production code.
+ jarjar_rules: "test-jarjar-rules.txt",
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
index 3c935058787b..702212b324f6 100644
--- a/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApCapabilityTest.java
@@ -16,14 +16,15 @@
package android.net.wifi;
-import android.net.wifi.util.SdkLevelUtil;
-import android.os.Parcel;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import android.os.Parcel;
+
import androidx.test.filters.SmallTest;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.Test;
/**
@@ -93,7 +94,7 @@ public class SoftApCapabilityTest {
@Test(expected = IllegalArgumentException.class)
public void testGetSupportedChannelListWithInvalidBand() {
- assumeTrue(SdkLevelUtil.isAtLeastS());
+ assumeTrue(SdkLevel.isAtLeastS());
long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT
| SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 7ee0fe858c5a..4a94f1f47962 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -22,12 +22,13 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import android.util.SparseIntArray;
import androidx.test.filters.SmallTest;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.Test;
import java.util.ArrayList;
@@ -84,7 +85,7 @@ public class SoftApConfigurationTest {
assertThat(original.getChannel()).isEqualTo(0);
assertThat(original.isHiddenSsid()).isEqualTo(false);
assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
- if (SdkLevelUtil.isAtLeastS()) {
+ if (SdkLevel.isAtLeastS()) {
assertThat(original.getMacRandomizationSetting())
.isEqualTo(SoftApConfiguration.RANDOMIZATION_PERSISTENT);
}
@@ -140,7 +141,7 @@ public class SoftApConfigurationTest {
.setClientControlByUserEnabled(true)
.setBlockedClientList(testBlockedClientList)
.setAllowedClientList(testAllowedClientList);
- if (SdkLevelUtil.isAtLeastS()) {
+ if (SdkLevel.isAtLeastS()) {
originalBuilder.setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE);
}
SoftApConfiguration original = originalBuilder.build();
@@ -156,7 +157,7 @@ public class SoftApConfigurationTest {
assertThat(original.isClientControlByUserEnabled()).isEqualTo(true);
assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList);
assertThat(original.getAllowedClientList()).isEqualTo(testAllowedClientList);
- if (SdkLevelUtil.isAtLeastS()) {
+ if (SdkLevel.isAtLeastS()) {
assertThat(original.getMacRandomizationSetting())
.isEqualTo(SoftApConfiguration.RANDOMIZATION_NONE);
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
index 2821c35f9347..28758432119c 100644
--- a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
@@ -16,14 +16,15 @@
package android.net.wifi;
+import static org.junit.Assert.assertEquals;
+
import android.net.MacAddress;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
-import static org.junit.Assert.assertEquals;
-
import androidx.test.filters.SmallTest;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.Test;
/**
@@ -84,7 +85,7 @@ public class SoftApInfoTest {
SoftApInfo info = new SoftApInfo();
assertEquals(info.getFrequency(), 0);
assertEquals(info.getBandwidth(), SoftApInfo.CHANNEL_WIDTH_INVALID);
- if (SdkLevelUtil.isAtLeastS()) {
+ if (SdkLevel.isAtLeastS()) {
assertEquals(info.getBssid(), null);
assertEquals(info.getWifiStandard(), ScanResult.WIFI_STANDARD_UNKNOWN);
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index bda776db733c..7340b145e8b2 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -86,7 +86,6 @@ import android.net.wifi.WifiManager.SoftApCallback;
import android.net.wifi.WifiManager.SuggestionConnectionStatusListener;
import android.net.wifi.WifiManager.TrafficStateCallback;
import android.net.wifi.WifiManager.WifiConnectedNetworkScorer;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -98,6 +97,8 @@ import android.util.SparseArray;
import androidx.test.filters.SmallTest;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -1729,7 +1730,7 @@ public class WifiManagerTest {
*/
@Test
public void testIsMultiStaConcurrencyOpenSupported() throws Exception {
- assumeTrue(SdkLevelUtil.isAtLeastS());
+ assumeTrue(SdkLevel.isAtLeastS());
when(mWifiService.getSupportedFeatures())
.thenReturn(new Long(WIFI_FEATURE_ADDITIONAL_STA));
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index bb84120da6ed..56e79983817f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -27,11 +27,12 @@ import static org.junit.Assume.assumeTrue;
import android.net.MacAddress;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.PasspointTestUtils;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.Test;
import java.security.cert.X509Certificate;
@@ -198,7 +199,7 @@ public class WifiNetworkSuggestionTest {
*/
@Test
public void testWifiNetworkSuggestionBuilderForOemPaidEnhancedOpenNetworkWithBssid() {
- assumeTrue(SdkLevelUtil.isAtLeastS());
+ assumeTrue(SdkLevel.isAtLeastS());
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
@@ -953,7 +954,7 @@ public class WifiNetworkSuggestionTest {
*/
@Test
public void testSimCredentialNetworkWithSubId() {
- assumeTrue(SdkLevelUtil.isAtLeastS());
+ assumeTrue(SdkLevel.isAtLeastS());
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM);
enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE);
@@ -1254,7 +1255,7 @@ public class WifiNetworkSuggestionTest {
*/
@Test
public void testSetIsNetworkAsOemPaid() {
- assumeTrue(SdkLevelUtil.isAtLeastS());
+ assumeTrue(SdkLevel.isAtLeastS());
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
@@ -1272,7 +1273,7 @@ public class WifiNetworkSuggestionTest {
*/
@Test
public void testSetIsNetworkAsOemPaidOnPasspointNetwork() {
- assumeTrue(SdkLevelUtil.isAtLeastS());
+ assumeTrue(SdkLevel.isAtLeastS());
PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
@@ -1307,7 +1308,7 @@ public class WifiNetworkSuggestionTest {
*/
@Test(expected = IllegalStateException.class)
public void testSetCredentialSharedWithUserWithSetIsNetworkAsOemPaid() {
- assumeTrue(SdkLevelUtil.isAtLeastS());
+ assumeTrue(SdkLevel.isAtLeastS());
new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 2cf7f2c1b8cf..d0d0c57008fc 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -39,7 +39,6 @@ import android.content.pm.PackageManager;
import android.net.MacAddress;
import android.net.wifi.RttManager;
import android.net.wifi.util.HexEncoding;
-import android.net.wifi.util.SdkLevelUtil;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -48,6 +47,8 @@ import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -164,7 +165,7 @@ public class WifiAwareManagerTest {
*/
@Test
public void testEnableInstantCommunicationMode() throws Exception {
- assumeTrue(SdkLevelUtil.isAtLeastS());
+ assumeTrue(SdkLevel.isAtLeastS());
mDut.isInstantCommunicationModeEnabled();
verify(mockAwareService).isInstantCommunicationModeEnabled();
mDut.enableInstantCommunicationMode(true);
diff --git a/wifi/tests/test-jarjar-rules.txt b/wifi/tests/test-jarjar-rules.txt
new file mode 100644
index 000000000000..41b97abb87b5
--- /dev/null
+++ b/wifi/tests/test-jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.android.modules.utils.** com.android.wifi.test.x.@0