Merge "bump tethering tc prio to make room for rate limiting"
diff --git a/OWNERS b/OWNERS
index 22b5561..07a775e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,4 @@
-codewiz@google.com
-jchalard@google.com
-junyulai@google.com
-lorenzo@google.com
-maze@google.com
-reminv@google.com
-satk@google.com
+set noparent
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+
+per-file **IpSec* = file:platform/frameworks/base:master:/services/core/java/com/android/server/vcn/OWNERS
\ No newline at end of file
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
new file mode 100644
index 0000000..bc1d002
--- /dev/null
+++ b/OWNERS_core_networking
@@ -0,0 +1,20 @@
+chenbruce@google.com
+chiachangwang@google.com
+cken@google.com
+huangaaron@google.com
+jchalard@google.com
+junyulai@google.com
+lifr@google.com
+lorenzo@google.com
+lucaslin@google.com
+markchien@google.com
+martinwu@google.com
+maze@google.com
+nuccachen@google.com
+paulhu@google.com
+prohr@google.com
+reminv@google.com
+satk@google.com
+waynema@google.com
+xiaom@google.com
+yumike@google.com
diff --git a/OWNERS_core_networking_xts b/OWNERS_core_networking_xts
new file mode 100644
index 0000000..a6627fe
--- /dev/null
+++ b/OWNERS_core_networking_xts
@@ -0,0 +1,2 @@
+lorenzo@google.com
+satk@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ebc1264..83619d6 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -2,3 +2,5 @@
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
+
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index be0e040..9f2ea35 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,6 +12,9 @@
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
}
]
},
@@ -25,6 +28,13 @@
"postsubmit": [
{
"name": "TetheringPrivilegedTests"
+ },
+ // TODO: move to presubmit when known green.
+ {
+ "name": "bpf_existence_test"
+ },
+ {
+ "name": "libclat_test"
}
],
"mainline-presubmit": [
@@ -33,6 +43,9 @@
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
}
]
},
@@ -48,6 +61,10 @@
},
{
"name": "TetheringCoverageTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
+ },
+ // TODO: move to mainline-presubmit when known green.
+ {
+ "name": "bpf_existence_test[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
}
],
"imports": [
@@ -55,6 +72,9 @@
"path": "frameworks/base/core/java/android/net"
},
{
+ "path": "frameworks/opt/net/ethernet"
+ },
+ {
"path": "packages/modules/NetworkStack"
},
{
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 60c1f2b..041a3ae 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -19,9 +19,14 @@
}
java_defaults {
- name: "TetheringAndroidLibraryDefaults",
+ name: "TetheringApiLevel",
sdk_version: "module_current",
+ target_sdk_version: "31",
min_sdk_version: "30",
+}
+
+java_defaults {
+ name: "TetheringAndroidLibraryDefaults",
srcs: [
"apishim/**/*.java",
"src/**/*.java",
@@ -30,15 +35,16 @@
":services-tethering-shared-srcs",
],
static_libs: [
- "NetworkStackApiStableShims",
"androidx.annotation_annotation",
"modules-utils-build",
+ "modules-utils-statemachine",
"networkstack-client",
"android.hardware.tetheroffload.config-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
"android.hardware.tetheroffload.control-V1.1-java",
"net-utils-framework-common",
"net-utils-device-common",
+ "net-utils-device-common-bpf",
"net-utils-device-common-netlink",
"netd-client",
],
@@ -51,18 +57,40 @@
],
plugins: ["java_api_finder"],
manifest: "AndroidManifestBase.xml",
+ lint: { strict_updatability_linting: true },
}
-// Build tethering static library, used to compile both variants of the tethering.
+// build tethering static library, used to compile both variants of the tethering.
android_library {
name: "TetheringApiCurrentLib",
- defaults: ["TetheringAndroidLibraryDefaults"],
+ defaults: [
+ "TetheringAndroidLibraryDefaults",
+ "TetheringApiLevel"
+ ],
+ static_libs: [
+ "NetworkStackApiCurrentShims",
+ ],
+ apex_available: ["com.android.tethering"],
+ lint: { strict_updatability_linting: true },
+}
+
+android_library {
+ name: "TetheringApiStableLib",
+ defaults: [
+ "TetheringAndroidLibraryDefaults",
+ "TetheringApiLevel"
+ ],
+ static_libs: [
+ "NetworkStackApiStableShims",
+ ],
+ apex_available: ["com.android.tethering"],
+ lint: { strict_updatability_linting: true },
}
// 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",
+ name: "libcom_android_networkstack_tethering_util_jni",
+ sdk_version: "30",
apex_available: [
"//apex_available:platform", // Used by InProcessTethering
"com.android.tethering",
@@ -70,7 +98,7 @@
min_sdk_version: "30",
header_libs: [
"bpf_syscall_wrappers",
- "bpf_tethering_headers",
+ "bpf_connectivity_headers",
],
srcs: [
"jni/*.cpp",
@@ -80,7 +108,9 @@
"libnativehelper_compat_libc++",
],
static_libs: [
+ "libnet_utils_device_common_bpfjni",
"libnetjniutils",
+ "libtcutils",
],
// We cannot use plain "libc++" here to link libc++ dynamically because it results in:
@@ -107,10 +137,9 @@
// Common defaults for compiling the actual APK.
java_defaults {
name: "TetheringAppDefaults",
- sdk_version: "module_current",
privileged: true,
jni_libs: [
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
],
resource_dirs: [
"res",
@@ -123,25 +152,46 @@
optimize: {
proguard_flags_files: ["proguard.flags"],
},
+ lint: { strict_updatability_linting: true },
}
// Non-updatable tethering running in the system server process for devices not using the module
android_app {
name: "InProcessTethering",
- defaults: ["TetheringAppDefaults"],
+ defaults: ["TetheringAppDefaults", "TetheringApiLevel"],
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: "30",
+ lint: { strict_updatability_linting: true },
}
-// Updatable tethering packaged as an application
+// Updatable tethering packaged for finalized API
android_app {
name: "Tethering",
- defaults: ["TetheringAppDefaults"],
+ defaults: ["TetheringAppDefaults", "TetheringApiLevel"],
+ static_libs: ["TetheringApiStableLib"],
+ certificate: "networkstack",
+ manifest: "AndroidManifest.xml",
+ use_embedded_native_libs: true,
+ // The permission configuration *must* be included to ensure security of the device
+ required: [
+ "NetworkPermissionConfig",
+ "privapp_whitelist_com.android.networkstack.tethering",
+ ],
+ apex_available: ["com.android.tethering"],
+ lint: { strict_updatability_linting: true },
+}
+
+android_app {
+ name: "TetheringNext",
+ defaults: [
+ "TetheringAppDefaults",
+ "TetheringApiLevel",
+ "ConnectivityNextEnableDefaults",
+ ],
static_libs: ["TetheringApiCurrentLib"],
certificate: "networkstack",
manifest: "AndroidManifest.xml",
@@ -152,10 +202,11 @@
"privapp_whitelist_com.android.networkstack.tethering",
],
apex_available: ["com.android.tethering"],
- min_sdk_version: "30",
+ lint: { strict_updatability_linting: true },
}
sdk {
name: "tethering-module-sdk",
bootclasspath_fragments: ["com.android.tethering-bootclasspath-fragment"],
+ systemserverclasspath_fragments: ["com.android.tethering-systemserverclasspath-fragment"],
}
diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml
index e6444f3..6deb345 100644
--- a/Tethering/AndroidManifest.xml
+++ b/Tethering/AndroidManifest.xml
@@ -19,14 +19,16 @@
<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. -->
+ added to the privileged permissions allowlist for that package. EntitlementManager
+ would set exact alarm but declare SCHEDULE_EXACT_ALARM is not necessary here because
+ privilege application would be in the allowlist. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<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" />
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 88c885a..72c83fa 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -18,28 +18,58 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// Defaults to enable/disable java targets which uses development APIs. "enabled" may have a
+// different value depending on the branch.
+java_defaults {
+ name: "ConnectivityNextEnableDefaults",
+ enabled: true,
+}
+apex_defaults {
+ name: "ConnectivityApexDefaults",
+ // Tethering app to include in the AOSP apex. Branches that disable the "next" targets may use
+ // a stable tethering app instead, but will generally override the AOSP apex to use updatable
+ // package names and keys, so that apex will be unused anyway.
+ apps: ["TetheringNext"], // Replace to "Tethering" if ConnectivityNextEnableDefaults is false.
+}
+// This is a placeholder comment to avoid merge conflicts
+// as the above target may have different "enabled" values
+// depending on the branch
+
apex {
name: "com.android.tethering",
- // TODO: make updatable again once this contains only updatable artifacts (in particular, this
- // cannot build as updatable unless service-connectivity builds against stable API).
- updatable: false,
- // min_sdk_version: "30",
+ defaults: ["ConnectivityApexDefaults"],
+ compile_multilib: "both",
+ updatable: true,
+ min_sdk_version: "30",
bootclasspath_fragments: [
"com.android.tethering-bootclasspath-fragment",
],
- java_libs: [
- "service-connectivity",
+ systemserverclasspath_fragments: [
+ "com.android.tethering-systemserverclasspath-fragment",
],
- jni_libs: [
- "libservice-connectivity",
+ multilib: {
+ first: {
+ jni_libs: [
+ "libservice-connectivity",
+ "libcom_android_connectivity_com_android_net_module_util_jni"
+ ],
+ },
+ both: {
+ jni_libs: ["libframework-connectivity-jni"],
+ },
+ },
+ binaries: [
+ "clatd",
],
+ canned_fs_config: "canned_fs_config",
bpfs: [
+ "clatd.o_mainline",
+ "netd.o_mainline",
"offload.o",
"test.o",
],
apps: [
"ServiceConnectivityResources",
- "Tethering",
],
prebuilts: ["current_sdkinfo"],
manifest: "manifest.json",
@@ -65,7 +95,11 @@
// Encapsulate the contributions made by the com.android.tethering to the bootclasspath.
bootclasspath_fragment {
name: "com.android.tethering-bootclasspath-fragment",
- contents: ["framework-tethering"],
+ contents: [
+ "framework-connectivity",
+ "framework-connectivity-tiramisu",
+ "framework-tethering",
+ ],
apex_available: ["com.android.tethering"],
// The bootclasspath_fragments that provide APIs on which this depends.
@@ -81,6 +115,25 @@
additional_stubs: [
"android-non-updatable",
],
+
+ // Additional hidden API flag files to override the defaults. This must only be
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_r_low_priority: [
+ "hiddenapi/hiddenapi-max-target-r-loprio.txt",
+ ],
+ max_target_o_low_priority: [
+ "hiddenapi/hiddenapi-max-target-o-low-priority.txt",
+ "hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt",
+ ],
+ unsupported: ["hiddenapi/hiddenapi-unsupported.txt"],
+ },
+}
+
+systemserverclasspath_fragment {
+ name: "com.android.tethering-systemserverclasspath-fragment",
+ standalone_contents: ["service-connectivity"],
+ apex_available: ["com.android.tethering"],
}
override_apex {
diff --git a/Tethering/apex/canned_fs_config b/Tethering/apex/canned_fs_config
new file mode 100644
index 0000000..44c57ab
--- /dev/null
+++ b/Tethering/apex/canned_fs_config
@@ -0,0 +1,2 @@
+/bin/for-system 0 1000 0550
+/bin/for-system/clatd 1029 1029 06555
diff --git a/Tethering/apex/hiddenapi/OWNERS b/Tethering/apex/hiddenapi/OWNERS
new file mode 100644
index 0000000..82b599d
--- /dev/null
+++ b/Tethering/apex/hiddenapi/OWNERS
@@ -0,0 +1,9 @@
+# These files are only intended to be changed by platform-compat and
+# the soong teams.
+set noparent
+
+# soong-team@ as the hiddenapi files are tightly coupled with Soong
+file:platform/build/soong:/OWNERS
+
+# compat-team@ for changes to hiddenapi files
+file:tools/platform-compat:/OWNERS
diff --git a/Tethering/apex/hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt b/Tethering/apex/hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt
new file mode 100644
index 0000000..88c77f2
--- /dev/null
+++ b/Tethering/apex/hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt
@@ -0,0 +1,87 @@
+Landroid/net/nsd/DnsSdTxtRecord;-><init>()V
+Landroid/net/nsd/DnsSdTxtRecord;-><init>(Landroid/net/nsd/DnsSdTxtRecord;)V
+Landroid/net/nsd/DnsSdTxtRecord;-><init>([B)V
+Landroid/net/nsd/DnsSdTxtRecord;->contains(Ljava/lang/String;)Z
+Landroid/net/nsd/DnsSdTxtRecord;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/net/nsd/DnsSdTxtRecord;->get(Ljava/lang/String;)Ljava/lang/String;
+Landroid/net/nsd/DnsSdTxtRecord;->getKey(I)Ljava/lang/String;
+Landroid/net/nsd/DnsSdTxtRecord;->getRawData()[B
+Landroid/net/nsd/DnsSdTxtRecord;->getValue(I)[B
+Landroid/net/nsd/DnsSdTxtRecord;->getValue(Ljava/lang/String;)[B
+Landroid/net/nsd/DnsSdTxtRecord;->getValueAsString(I)Ljava/lang/String;
+Landroid/net/nsd/DnsSdTxtRecord;->insert([B[BI)V
+Landroid/net/nsd/DnsSdTxtRecord;->keyCount()I
+Landroid/net/nsd/DnsSdTxtRecord;->mData:[B
+Landroid/net/nsd/DnsSdTxtRecord;->mSeperator:B
+Landroid/net/nsd/DnsSdTxtRecord;->remove(Ljava/lang/String;)I
+Landroid/net/nsd/DnsSdTxtRecord;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/net/nsd/DnsSdTxtRecord;->size()I
+Landroid/net/nsd/INsdManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/net/nsd/INsdManager$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/net/nsd/INsdManager$Stub$Proxy;->getMessenger()Landroid/os/Messenger;
+Landroid/net/nsd/INsdManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/net/nsd/INsdManager$Stub$Proxy;->setEnabled(Z)V
+Landroid/net/nsd/INsdManager$Stub;-><init>()V
+Landroid/net/nsd/INsdManager$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/net/nsd/INsdManager$Stub;->TRANSACTION_getMessenger:I
+Landroid/net/nsd/INsdManager$Stub;->TRANSACTION_setEnabled:I
+Landroid/net/nsd/INsdManager;->setEnabled(Z)V
+Landroid/net/nsd/NsdManager;-><init>(Landroid/content/Context;Landroid/net/nsd/INsdManager;)V
+Landroid/net/nsd/NsdManager;->BASE:I
+Landroid/net/nsd/NsdManager;->checkListener(Ljava/lang/Object;)V
+Landroid/net/nsd/NsdManager;->checkProtocol(I)V
+Landroid/net/nsd/NsdManager;->checkServiceInfo(Landroid/net/nsd/NsdServiceInfo;)V
+Landroid/net/nsd/NsdManager;->DBG:Z
+Landroid/net/nsd/NsdManager;->DISABLE:I
+Landroid/net/nsd/NsdManager;->disconnect()V
+Landroid/net/nsd/NsdManager;->DISCOVER_SERVICES:I
+Landroid/net/nsd/NsdManager;->DISCOVER_SERVICES_FAILED:I
+Landroid/net/nsd/NsdManager;->DISCOVER_SERVICES_STARTED:I
+Landroid/net/nsd/NsdManager;->ENABLE:I
+Landroid/net/nsd/NsdManager;->EVENT_NAMES:Landroid/util/SparseArray;
+Landroid/net/nsd/NsdManager;->fatal(Ljava/lang/String;)V
+Landroid/net/nsd/NsdManager;->FIRST_LISTENER_KEY:I
+Landroid/net/nsd/NsdManager;->getListenerKey(Ljava/lang/Object;)I
+Landroid/net/nsd/NsdManager;->getMessenger()Landroid/os/Messenger;
+Landroid/net/nsd/NsdManager;->getNsdServiceInfoType(Landroid/net/nsd/NsdServiceInfo;)Ljava/lang/String;
+Landroid/net/nsd/NsdManager;->init()V
+Landroid/net/nsd/NsdManager;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel;
+Landroid/net/nsd/NsdManager;->mConnected:Ljava/util/concurrent/CountDownLatch;
+Landroid/net/nsd/NsdManager;->mContext:Landroid/content/Context;
+Landroid/net/nsd/NsdManager;->mHandler:Landroid/net/nsd/NsdManager$ServiceHandler;
+Landroid/net/nsd/NsdManager;->mListenerKey:I
+Landroid/net/nsd/NsdManager;->mListenerMap:Landroid/util/SparseArray;
+Landroid/net/nsd/NsdManager;->mMapLock:Ljava/lang/Object;
+Landroid/net/nsd/NsdManager;->mService:Landroid/net/nsd/INsdManager;
+Landroid/net/nsd/NsdManager;->mServiceMap:Landroid/util/SparseArray;
+Landroid/net/nsd/NsdManager;->nameOf(I)Ljava/lang/String;
+Landroid/net/nsd/NsdManager;->NATIVE_DAEMON_EVENT:I
+Landroid/net/nsd/NsdManager;->nextListenerKey()I
+Landroid/net/nsd/NsdManager;->putListener(Ljava/lang/Object;Landroid/net/nsd/NsdServiceInfo;)I
+Landroid/net/nsd/NsdManager;->REGISTER_SERVICE:I
+Landroid/net/nsd/NsdManager;->REGISTER_SERVICE_FAILED:I
+Landroid/net/nsd/NsdManager;->REGISTER_SERVICE_SUCCEEDED:I
+Landroid/net/nsd/NsdManager;->removeListener(I)V
+Landroid/net/nsd/NsdManager;->RESOLVE_SERVICE:I
+Landroid/net/nsd/NsdManager;->RESOLVE_SERVICE_FAILED:I
+Landroid/net/nsd/NsdManager;->RESOLVE_SERVICE_SUCCEEDED:I
+Landroid/net/nsd/NsdManager;->SERVICE_FOUND:I
+Landroid/net/nsd/NsdManager;->SERVICE_LOST:I
+Landroid/net/nsd/NsdManager;->setEnabled(Z)V
+Landroid/net/nsd/NsdManager;->STOP_DISCOVERY:I
+Landroid/net/nsd/NsdManager;->STOP_DISCOVERY_FAILED:I
+Landroid/net/nsd/NsdManager;->STOP_DISCOVERY_SUCCEEDED:I
+Landroid/net/nsd/NsdManager;->TAG:Ljava/lang/String;
+Landroid/net/nsd/NsdManager;->UNREGISTER_SERVICE:I
+Landroid/net/nsd/NsdManager;->UNREGISTER_SERVICE_FAILED:I
+Landroid/net/nsd/NsdManager;->UNREGISTER_SERVICE_SUCCEEDED:I
+Landroid/net/nsd/NsdServiceInfo;-><init>(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/net/nsd/NsdServiceInfo;->getTxtRecord()[B
+Landroid/net/nsd/NsdServiceInfo;->getTxtRecordSize()I
+Landroid/net/nsd/NsdServiceInfo;->mHost:Ljava/net/InetAddress;
+Landroid/net/nsd/NsdServiceInfo;->mPort:I
+Landroid/net/nsd/NsdServiceInfo;->mServiceName:Ljava/lang/String;
+Landroid/net/nsd/NsdServiceInfo;->mServiceType:Ljava/lang/String;
+Landroid/net/nsd/NsdServiceInfo;->mTxtRecord:Landroid/util/ArrayMap;
+Landroid/net/nsd/NsdServiceInfo;->setTxtRecords(Ljava/lang/String;)V
+Landroid/net/nsd/NsdServiceInfo;->TAG:Ljava/lang/String;
diff --git a/Tethering/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt b/Tethering/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt
new file mode 100644
index 0000000..1f49d1b
--- /dev/null
+++ b/Tethering/apex/hiddenapi/hiddenapi-max-target-o-low-priority.txt
@@ -0,0 +1,1216 @@
+Landroid/net/CaptivePortal;-><init>(Landroid/os/IBinder;)V
+Landroid/net/CaptivePortal;->APP_RETURN_DISMISSED:I
+Landroid/net/CaptivePortal;->APP_RETURN_UNWANTED:I
+Landroid/net/CaptivePortal;->APP_RETURN_WANTED_AS_IS:I
+Landroid/net/CaptivePortal;->mBinder:Landroid/os/IBinder;
+Landroid/net/CaptivePortal;->useNetwork()V
+Landroid/net/ConnectivityManager$CallbackHandler;->DBG:Z
+Landroid/net/ConnectivityManager$CallbackHandler;->getObject(Landroid/os/Message;Ljava/lang/Class;)Ljava/lang/Object;
+Landroid/net/ConnectivityManager$CallbackHandler;->TAG:Ljava/lang/String;
+Landroid/net/ConnectivityManager$Errors;->TOO_MANY_REQUESTS:I
+Landroid/net/ConnectivityManager$LegacyRequest;-><init>()V
+Landroid/net/ConnectivityManager$LegacyRequest;->clearDnsBinding()V
+Landroid/net/ConnectivityManager$LegacyRequest;->currentNetwork:Landroid/net/Network;
+Landroid/net/ConnectivityManager$LegacyRequest;->delay:I
+Landroid/net/ConnectivityManager$LegacyRequest;->expireSequenceNumber:I
+Landroid/net/ConnectivityManager$LegacyRequest;->networkCallback:Landroid/net/ConnectivityManager$NetworkCallback;
+Landroid/net/ConnectivityManager$LegacyRequest;->networkCapabilities:Landroid/net/NetworkCapabilities;
+Landroid/net/ConnectivityManager$LegacyRequest;->networkRequest:Landroid/net/NetworkRequest;
+Landroid/net/ConnectivityManager$NetworkCallback;->networkRequest:Landroid/net/NetworkRequest;
+Landroid/net/ConnectivityManager$NetworkCallback;->onAvailable(Landroid/net/Network;Landroid/net/NetworkCapabilities;Landroid/net/LinkProperties;)V
+Landroid/net/ConnectivityManager$NetworkCallback;->onNetworkResumed(Landroid/net/Network;)V
+Landroid/net/ConnectivityManager$NetworkCallback;->onNetworkSuspended(Landroid/net/Network;)V
+Landroid/net/ConnectivityManager$NetworkCallback;->onPreCheck(Landroid/net/Network;)V
+Landroid/net/ConnectivityManager$PacketKeepalive;->BINDER_DIED:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_HARDWARE_ERROR:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_HARDWARE_UNSUPPORTED:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_INTERVAL:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_IP_ADDRESS:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_LENGTH:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_NETWORK:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_PORT:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->mCallback:Landroid/net/ConnectivityManager$PacketKeepaliveCallback;
+Landroid/net/ConnectivityManager$PacketKeepalive;->MIN_INTERVAL:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->mLooper:Landroid/os/Looper;
+Landroid/net/ConnectivityManager$PacketKeepalive;->mMessenger:Landroid/os/Messenger;
+Landroid/net/ConnectivityManager$PacketKeepalive;->mNetwork:Landroid/net/Network;
+Landroid/net/ConnectivityManager$PacketKeepalive;->mSlot:Ljava/lang/Integer;
+Landroid/net/ConnectivityManager$PacketKeepalive;->NATT_PORT:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->NO_KEEPALIVE:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->stopLooper()V
+Landroid/net/ConnectivityManager$PacketKeepalive;->SUCCESS:I
+Landroid/net/ConnectivityManager$PacketKeepalive;->TAG:Ljava/lang/String;
+Landroid/net/ConnectivityManager$TooManyRequestsException;-><init>()V
+Landroid/net/ConnectivityManager;-><init>(Landroid/content/Context;Landroid/net/IConnectivityManager;)V
+Landroid/net/ConnectivityManager;->ACTION_CAPTIVE_PORTAL_TEST_COMPLETED:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->ACTION_DATA_ACTIVITY_CHANGE:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->ACTION_PROMPT_LOST_VALIDATION:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->ACTION_PROMPT_UNVALIDATED:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->ALREADY_UNREGISTERED:Landroid/net/NetworkRequest;
+Landroid/net/ConnectivityManager;->BASE:I
+Landroid/net/ConnectivityManager;->CALLBACK_AVAILABLE:I
+Landroid/net/ConnectivityManager;->CALLBACK_CAP_CHANGED:I
+Landroid/net/ConnectivityManager;->CALLBACK_IP_CHANGED:I
+Landroid/net/ConnectivityManager;->CALLBACK_LOSING:I
+Landroid/net/ConnectivityManager;->CALLBACK_LOST:I
+Landroid/net/ConnectivityManager;->CALLBACK_PRECHECK:I
+Landroid/net/ConnectivityManager;->CALLBACK_RESUMED:I
+Landroid/net/ConnectivityManager;->CALLBACK_SUSPENDED:I
+Landroid/net/ConnectivityManager;->CALLBACK_UNAVAIL:I
+Landroid/net/ConnectivityManager;->checkCallbackNotNull(Landroid/net/ConnectivityManager$NetworkCallback;)V
+Landroid/net/ConnectivityManager;->checkLegacyRoutingApiAccess()V
+Landroid/net/ConnectivityManager;->checkMobileProvisioning(I)I
+Landroid/net/ConnectivityManager;->checkPendingIntentNotNull(Landroid/app/PendingIntent;)V
+Landroid/net/ConnectivityManager;->checkTimeout(I)V
+Landroid/net/ConnectivityManager;->CONNECTIVITY_ACTION_SUPL:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->convertServiceException(Landroid/os/ServiceSpecificException;)Ljava/lang/RuntimeException;
+Landroid/net/ConnectivityManager;->enforceChangePermission(Landroid/content/Context;)V
+Landroid/net/ConnectivityManager;->enforceTetherChangePermission(Landroid/content/Context;Ljava/lang/String;)V
+Landroid/net/ConnectivityManager;->expireRequest(Landroid/net/NetworkCapabilities;I)V
+Landroid/net/ConnectivityManager;->EXPIRE_LEGACY_REQUEST:I
+Landroid/net/ConnectivityManager;->EXTRA_ACTIVE_LOCAL_ONLY:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_ADD_TETHER_TYPE:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_CAPTIVE_PORTAL_PROBE_SPEC:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_CAPTIVE_PORTAL_USER_AGENT:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_DEVICE_TYPE:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_INET_CONDITION:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_IS_ACTIVE:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_IS_CAPTIVE_PORTAL:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_PROVISION_CALLBACK:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_REALTIME_NS:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_REM_TETHER_TYPE:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_RUN_PROVISION:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->EXTRA_SET_ALARM:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->factoryReset()V
+Landroid/net/ConnectivityManager;->findRequestForFeature(Landroid/net/NetworkCapabilities;)Landroid/net/NetworkRequest;
+Landroid/net/ConnectivityManager;->getActiveNetworkForUid(I)Landroid/net/Network;
+Landroid/net/ConnectivityManager;->getActiveNetworkForUid(IZ)Landroid/net/Network;
+Landroid/net/ConnectivityManager;->getActiveNetworkInfoForUid(IZ)Landroid/net/NetworkInfo;
+Landroid/net/ConnectivityManager;->getAlwaysOnVpnPackageForUser(I)Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getCallbackName(I)Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getDefaultHandler()Landroid/net/ConnectivityManager$CallbackHandler;
+Landroid/net/ConnectivityManager;->getGlobalProxy()Landroid/net/ProxyInfo;
+Landroid/net/ConnectivityManager;->getInstanceOrNull()Landroid/net/ConnectivityManager;
+Landroid/net/ConnectivityManager;->getMobileProvisioningUrl()Ljava/lang/String;
+Landroid/net/ConnectivityManager;->getNetworkInfoForUid(Landroid/net/Network;IZ)Landroid/net/NetworkInfo;
+Landroid/net/ConnectivityManager;->getNetworkManagementService()Landroid/os/INetworkManagementService;
+Landroid/net/ConnectivityManager;->getNetworkPolicyManager()Landroid/net/INetworkPolicyManager;
+Landroid/net/ConnectivityManager;->getProxyForNetwork(Landroid/net/Network;)Landroid/net/ProxyInfo;
+Landroid/net/ConnectivityManager;->getTetheredDhcpRanges()[Ljava/lang/String;
+Landroid/net/ConnectivityManager;->inferLegacyTypeForNetworkCapabilities(Landroid/net/NetworkCapabilities;)I
+Landroid/net/ConnectivityManager;->isAlwaysOnVpnPackageSupportedForUser(ILjava/lang/String;)Z
+Landroid/net/ConnectivityManager;->isNetworkTypeWifi(I)Z
+Landroid/net/ConnectivityManager;->legacyTypeForNetworkCapabilities(Landroid/net/NetworkCapabilities;)I
+Landroid/net/ConnectivityManager;->LISTEN:I
+Landroid/net/ConnectivityManager;->MAX_NETWORK_TYPE:I
+Landroid/net/ConnectivityManager;->MAX_RADIO_TYPE:I
+Landroid/net/ConnectivityManager;->mContext:Landroid/content/Context;
+Landroid/net/ConnectivityManager;->MIN_NETWORK_TYPE:I
+Landroid/net/ConnectivityManager;->mNetworkActivityListeners:Landroid/util/ArrayMap;
+Landroid/net/ConnectivityManager;->mNMService:Landroid/os/INetworkManagementService;
+Landroid/net/ConnectivityManager;->mNPManager:Landroid/net/INetworkPolicyManager;
+Landroid/net/ConnectivityManager;->MULTIPATH_PREFERENCE_UNMETERED:I
+Landroid/net/ConnectivityManager;->NETID_UNSET:I
+Landroid/net/ConnectivityManager;->networkCapabilitiesForType(I)Landroid/net/NetworkCapabilities;
+Landroid/net/ConnectivityManager;->PRIVATE_DNS_DEFAULT_MODE_FALLBACK:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->PRIVATE_DNS_MODE_OFF:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->PRIVATE_DNS_MODE_OPPORTUNISTIC:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->registerNetworkAgent(Landroid/os/Messenger;Landroid/net/NetworkInfo;Landroid/net/LinkProperties;Landroid/net/NetworkCapabilities;ILandroid/net/NetworkMisc;)I
+Landroid/net/ConnectivityManager;->renewRequestLocked(Landroid/net/ConnectivityManager$LegacyRequest;)V
+Landroid/net/ConnectivityManager;->reportInetCondition(II)V
+Landroid/net/ConnectivityManager;->REQUEST:I
+Landroid/net/ConnectivityManager;->requestNetwork(Landroid/net/NetworkRequest;Landroid/net/ConnectivityManager$NetworkCallback;IILandroid/os/Handler;)V
+Landroid/net/ConnectivityManager;->REQUEST_ID_UNSET:I
+Landroid/net/ConnectivityManager;->sCallbackHandler:Landroid/net/ConnectivityManager$CallbackHandler;
+Landroid/net/ConnectivityManager;->sCallbacks:Ljava/util/HashMap;
+Landroid/net/ConnectivityManager;->sendExpireMsgForFeature(Landroid/net/NetworkCapabilities;II)V
+Landroid/net/ConnectivityManager;->sendRequestForNetwork(Landroid/net/NetworkCapabilities;Landroid/net/ConnectivityManager$NetworkCallback;IIILandroid/net/ConnectivityManager$CallbackHandler;)Landroid/net/NetworkRequest;
+Landroid/net/ConnectivityManager;->setAcceptUnvalidated(Landroid/net/Network;ZZ)V
+Landroid/net/ConnectivityManager;->setAlwaysOnVpnPackageForUser(ILjava/lang/String;Z)Z
+Landroid/net/ConnectivityManager;->setAvoidUnvalidated(Landroid/net/Network;)V
+Landroid/net/ConnectivityManager;->setGlobalProxy(Landroid/net/ProxyInfo;)V
+Landroid/net/ConnectivityManager;->setProvisioningNotificationVisible(ZILjava/lang/String;)V
+Landroid/net/ConnectivityManager;->sInstance:Landroid/net/ConnectivityManager;
+Landroid/net/ConnectivityManager;->sLegacyTypeToCapability:Landroid/util/SparseIntArray;
+Landroid/net/ConnectivityManager;->sLegacyTypeToTransport:Landroid/util/SparseIntArray;
+Landroid/net/ConnectivityManager;->startCaptivePortalApp(Landroid/net/Network;)V
+Landroid/net/ConnectivityManager;->TAG:Ljava/lang/String;
+Landroid/net/ConnectivityManager;->TETHERING_INVALID:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_DISABLE_NAT_ERROR:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_ENABLE_NAT_ERROR:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_IFACE_CFG_ERROR:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_MASTER_ERROR:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_NO_ERROR:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_PROVISION_FAILED:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_SERVICE_UNAVAIL:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_TETHER_IFACE_ERROR:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_UNAVAIL_IFACE:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_UNKNOWN_IFACE:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_UNSUPPORTED:I
+Landroid/net/ConnectivityManager;->TETHER_ERROR_UNTETHER_IFACE_ERROR:I
+Landroid/net/ConnectivityManager;->unsupportedStartingFrom(I)V
+Landroid/net/ConnectivityManager;->updateLockdownVpn()Z
+Landroid/net/ConnectivityThread$Singleton;-><init>()V
+Landroid/net/ConnectivityThread$Singleton;->INSTANCE:Landroid/net/ConnectivityThread;
+Landroid/net/ConnectivityThread;-><init>()V
+Landroid/net/ConnectivityThread;->createInstance()Landroid/net/ConnectivityThread;
+Landroid/net/ConnectivityThread;->get()Landroid/net/ConnectivityThread;
+Landroid/net/ConnectivityThread;->getInstanceLooper()Landroid/os/Looper;
+Landroid/net/DhcpInfo;-><init>(Landroid/net/DhcpInfo;)V
+Landroid/net/DhcpInfo;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/net/DhcpInfo;->putAddress(Ljava/lang/StringBuffer;I)V
+Landroid/net/ICaptivePortal$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/net/ICaptivePortal$Stub$Proxy;->appResponse(I)V
+Landroid/net/ICaptivePortal$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/net/ICaptivePortal$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/net/ICaptivePortal$Stub;-><init>()V
+Landroid/net/ICaptivePortal$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/ICaptivePortal;
+Landroid/net/ICaptivePortal$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/net/ICaptivePortal$Stub;->TRANSACTION_appResponse:I
+Landroid/net/ICaptivePortal;->appResponse(I)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->addVpnAddress(Ljava/lang/String;I)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->checkMobileProvisioning(I)I
+Landroid/net/IConnectivityManager$Stub$Proxy;->establishVpn(Lcom/android/internal/net/VpnConfig;)Landroid/os/ParcelFileDescriptor;
+Landroid/net/IConnectivityManager$Stub$Proxy;->factoryReset()V
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetwork()Landroid/net/Network;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkForUid(IZ)Landroid/net/Network;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfoForUid(IZ)Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkQuotaInfo()Landroid/net/NetworkQuotaInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworkState()[Landroid/net/NetworkState;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getAllVpnInfo()[Lcom/android/internal/net/VpnInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getAlwaysOnVpnPackage(I)Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getCaptivePortalServerUrl()Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getDefaultNetworkCapabilitiesForUser(I)[Landroid/net/NetworkCapabilities;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getGlobalProxy()Landroid/net/ProxyInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getLastTetherError(Ljava/lang/String;)I
+Landroid/net/IConnectivityManager$Stub$Proxy;->getLegacyVpnInfo(I)Lcom/android/internal/net/LegacyVpnInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getLinkProperties(Landroid/net/Network;)Landroid/net/LinkProperties;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getLinkPropertiesForType(I)Landroid/net/LinkProperties;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getMobileProvisioningUrl()Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getMultipathPreference(Landroid/net/Network;)I
+Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkCapabilities(Landroid/net/Network;)Landroid/net/NetworkCapabilities;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkForType(I)Landroid/net/Network;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkInfo(I)Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkInfoForUid(Landroid/net/Network;IZ)Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkWatchlistConfigHash()[B
+Landroid/net/IConnectivityManager$Stub$Proxy;->getProxyForNetwork(Landroid/net/Network;)Landroid/net/ProxyInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getRestoreDefaultNetworkDelay(I)I
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableBluetoothRegexs()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableWifiRegexs()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredDhcpRanges()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheringErroredIfaces()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getVpnConfig(I)Lcom/android/internal/net/VpnConfig;
+Landroid/net/IConnectivityManager$Stub$Proxy;->isActiveNetworkMetered()Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->isAlwaysOnVpnPackageSupported(ILjava/lang/String;)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->isNetworkSupported(I)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->isTetheringSupported(Ljava/lang/String;)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->listenForNetwork(Landroid/net/NetworkCapabilities;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/NetworkRequest;
+Landroid/net/IConnectivityManager$Stub$Proxy;->pendingListenForNetwork(Landroid/net/NetworkCapabilities;Landroid/app/PendingIntent;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->pendingRequestForNetwork(Landroid/net/NetworkCapabilities;Landroid/app/PendingIntent;)Landroid/net/NetworkRequest;
+Landroid/net/IConnectivityManager$Stub$Proxy;->prepareVpn(Ljava/lang/String;Ljava/lang/String;I)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->registerNetworkAgent(Landroid/os/Messenger;Landroid/net/NetworkInfo;Landroid/net/LinkProperties;Landroid/net/NetworkCapabilities;ILandroid/net/NetworkMisc;)I
+Landroid/net/IConnectivityManager$Stub$Proxy;->registerNetworkFactory(Landroid/os/Messenger;Ljava/lang/String;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->releaseNetworkRequest(Landroid/net/NetworkRequest;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->releasePendingNetworkRequest(Landroid/app/PendingIntent;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->removeVpnAddress(Ljava/lang/String;I)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->reportInetCondition(II)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->reportNetworkConnectivity(Landroid/net/Network;Z)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->requestBandwidthUpdate(Landroid/net/Network;)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->requestNetwork(Landroid/net/NetworkCapabilities;Landroid/os/Messenger;ILandroid/os/IBinder;I)Landroid/net/NetworkRequest;
+Landroid/net/IConnectivityManager$Stub$Proxy;->requestRouteToHostAddress(I[B)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->setAcceptUnvalidated(Landroid/net/Network;ZZ)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->setAirplaneMode(Z)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->setAlwaysOnVpnPackage(ILjava/lang/String;Z)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->setAvoidUnvalidated(Landroid/net/Network;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->setGlobalProxy(Landroid/net/ProxyInfo;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->setProvisioningNotificationVisible(ZILjava/lang/String;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->setUnderlyingNetworksForVpn([Landroid/net/Network;)Z
+Landroid/net/IConnectivityManager$Stub$Proxy;->setUsbTethering(ZLjava/lang/String;)I
+Landroid/net/IConnectivityManager$Stub$Proxy;->setVpnPackageAuthorization(Ljava/lang/String;IZ)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->startCaptivePortalApp(Landroid/net/Network;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->startNattKeepalive(Landroid/net/Network;ILandroid/os/Messenger;Landroid/os/IBinder;Ljava/lang/String;ILjava/lang/String;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->startTethering(ILandroid/os/ResultReceiver;ZLjava/lang/String;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->stopKeepalive(Landroid/net/Network;I)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->stopTethering(ILjava/lang/String;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->tether(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/net/IConnectivityManager$Stub$Proxy;->unregisterNetworkFactory(Landroid/os/Messenger;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->untether(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/net/IConnectivityManager$Stub$Proxy;->updateLockdownVpn()Z
+Landroid/net/IConnectivityManager$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_addVpnAddress:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_checkMobileProvisioning:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_establishVpn:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_factoryReset:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveLinkProperties:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetwork:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetworkForUid:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetworkInfo:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetworkInfoForUid:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetworkQuotaInfo:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAllNetworkInfo:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAllNetworks:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAllNetworkState:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAllVpnInfo:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAlwaysOnVpnPackage:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getCaptivePortalServerUrl:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getDefaultNetworkCapabilitiesForUser:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getGlobalProxy:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getLastTetherError:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getLegacyVpnInfo:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getLinkProperties:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getLinkPropertiesForType:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getMobileProvisioningUrl:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getMultipathPreference:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkCapabilities:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkForType:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkInfo:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkInfoForUid:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkWatchlistConfigHash:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getProxyForNetwork:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getRestoreDefaultNetworkDelay:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetherableBluetoothRegexs:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetherableIfaces:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetherableUsbRegexs:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetherableWifiRegexs:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetheredDhcpRanges:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetheredIfaces:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetheringErroredIfaces:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getVpnConfig:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_isActiveNetworkMetered:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_isAlwaysOnVpnPackageSupported:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_isNetworkSupported:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_isTetheringSupported:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_listenForNetwork:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_pendingListenForNetwork:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_pendingRequestForNetwork:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_prepareVpn:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_registerNetworkAgent:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_registerNetworkFactory:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_releaseNetworkRequest:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_releasePendingNetworkRequest:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_removeVpnAddress:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_reportInetCondition:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_reportNetworkConnectivity:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_requestBandwidthUpdate:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_requestNetwork:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_requestRouteToHostAddress:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setAcceptUnvalidated:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setAirplaneMode:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setAlwaysOnVpnPackage:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setAvoidUnvalidated:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setGlobalProxy:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setProvisioningNotificationVisible:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setUnderlyingNetworksForVpn:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setUsbTethering:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setVpnPackageAuthorization:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_startCaptivePortalApp:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_startLegacyVpn:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_startNattKeepalive:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_startTethering:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_stopKeepalive:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_stopTethering:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_tether:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_unregisterNetworkFactory:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_untether:I
+Landroid/net/IConnectivityManager$Stub;->TRANSACTION_updateLockdownVpn:I
+Landroid/net/IConnectivityManager;->addVpnAddress(Ljava/lang/String;I)Z
+Landroid/net/IConnectivityManager;->checkMobileProvisioning(I)I
+Landroid/net/IConnectivityManager;->establishVpn(Lcom/android/internal/net/VpnConfig;)Landroid/os/ParcelFileDescriptor;
+Landroid/net/IConnectivityManager;->factoryReset()V
+Landroid/net/IConnectivityManager;->getActiveNetwork()Landroid/net/Network;
+Landroid/net/IConnectivityManager;->getActiveNetworkForUid(IZ)Landroid/net/Network;
+Landroid/net/IConnectivityManager;->getActiveNetworkInfoForUid(IZ)Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager;->getActiveNetworkQuotaInfo()Landroid/net/NetworkQuotaInfo;
+Landroid/net/IConnectivityManager;->getAllNetworks()[Landroid/net/Network;
+Landroid/net/IConnectivityManager;->getAllVpnInfo()[Lcom/android/internal/net/VpnInfo;
+Landroid/net/IConnectivityManager;->getAlwaysOnVpnPackage(I)Ljava/lang/String;
+Landroid/net/IConnectivityManager;->getCaptivePortalServerUrl()Ljava/lang/String;
+Landroid/net/IConnectivityManager;->getDefaultNetworkCapabilitiesForUser(I)[Landroid/net/NetworkCapabilities;
+Landroid/net/IConnectivityManager;->getGlobalProxy()Landroid/net/ProxyInfo;
+Landroid/net/IConnectivityManager;->getLegacyVpnInfo(I)Lcom/android/internal/net/LegacyVpnInfo;
+Landroid/net/IConnectivityManager;->getLinkProperties(Landroid/net/Network;)Landroid/net/LinkProperties;
+Landroid/net/IConnectivityManager;->getLinkPropertiesForType(I)Landroid/net/LinkProperties;
+Landroid/net/IConnectivityManager;->getMobileProvisioningUrl()Ljava/lang/String;
+Landroid/net/IConnectivityManager;->getMultipathPreference(Landroid/net/Network;)I
+Landroid/net/IConnectivityManager;->getNetworkCapabilities(Landroid/net/Network;)Landroid/net/NetworkCapabilities;
+Landroid/net/IConnectivityManager;->getNetworkForType(I)Landroid/net/Network;
+Landroid/net/IConnectivityManager;->getNetworkInfoForUid(Landroid/net/Network;IZ)Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager;->getNetworkWatchlistConfigHash()[B
+Landroid/net/IConnectivityManager;->getProxyForNetwork(Landroid/net/Network;)Landroid/net/ProxyInfo;
+Landroid/net/IConnectivityManager;->getRestoreDefaultNetworkDelay(I)I
+Landroid/net/IConnectivityManager;->getTetherableBluetoothRegexs()[Ljava/lang/String;
+Landroid/net/IConnectivityManager;->getTetheredDhcpRanges()[Ljava/lang/String;
+Landroid/net/IConnectivityManager;->getVpnConfig(I)Lcom/android/internal/net/VpnConfig;
+Landroid/net/IConnectivityManager;->isActiveNetworkMetered()Z
+Landroid/net/IConnectivityManager;->isAlwaysOnVpnPackageSupported(ILjava/lang/String;)Z
+Landroid/net/IConnectivityManager;->isNetworkSupported(I)Z
+Landroid/net/IConnectivityManager;->isTetheringSupported(Ljava/lang/String;)Z
+Landroid/net/IConnectivityManager;->listenForNetwork(Landroid/net/NetworkCapabilities;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/NetworkRequest;
+Landroid/net/IConnectivityManager;->pendingListenForNetwork(Landroid/net/NetworkCapabilities;Landroid/app/PendingIntent;)V
+Landroid/net/IConnectivityManager;->pendingRequestForNetwork(Landroid/net/NetworkCapabilities;Landroid/app/PendingIntent;)Landroid/net/NetworkRequest;
+Landroid/net/IConnectivityManager;->prepareVpn(Ljava/lang/String;Ljava/lang/String;I)Z
+Landroid/net/IConnectivityManager;->registerNetworkAgent(Landroid/os/Messenger;Landroid/net/NetworkInfo;Landroid/net/LinkProperties;Landroid/net/NetworkCapabilities;ILandroid/net/NetworkMisc;)I
+Landroid/net/IConnectivityManager;->registerNetworkFactory(Landroid/os/Messenger;Ljava/lang/String;)V
+Landroid/net/IConnectivityManager;->releaseNetworkRequest(Landroid/net/NetworkRequest;)V
+Landroid/net/IConnectivityManager;->releasePendingNetworkRequest(Landroid/app/PendingIntent;)V
+Landroid/net/IConnectivityManager;->removeVpnAddress(Ljava/lang/String;I)Z
+Landroid/net/IConnectivityManager;->reportNetworkConnectivity(Landroid/net/Network;Z)V
+Landroid/net/IConnectivityManager;->requestBandwidthUpdate(Landroid/net/Network;)Z
+Landroid/net/IConnectivityManager;->requestNetwork(Landroid/net/NetworkCapabilities;Landroid/os/Messenger;ILandroid/os/IBinder;I)Landroid/net/NetworkRequest;
+Landroid/net/IConnectivityManager;->requestRouteToHostAddress(I[B)Z
+Landroid/net/IConnectivityManager;->setAcceptUnvalidated(Landroid/net/Network;ZZ)V
+Landroid/net/IConnectivityManager;->setAlwaysOnVpnPackage(ILjava/lang/String;Z)Z
+Landroid/net/IConnectivityManager;->setAvoidUnvalidated(Landroid/net/Network;)V
+Landroid/net/IConnectivityManager;->setGlobalProxy(Landroid/net/ProxyInfo;)V
+Landroid/net/IConnectivityManager;->setProvisioningNotificationVisible(ZILjava/lang/String;)V
+Landroid/net/IConnectivityManager;->setUnderlyingNetworksForVpn([Landroid/net/Network;)Z
+Landroid/net/IConnectivityManager;->setUsbTethering(ZLjava/lang/String;)I
+Landroid/net/IConnectivityManager;->setVpnPackageAuthorization(Ljava/lang/String;IZ)V
+Landroid/net/IConnectivityManager;->startCaptivePortalApp(Landroid/net/Network;)V
+Landroid/net/IConnectivityManager;->startNattKeepalive(Landroid/net/Network;ILandroid/os/Messenger;Landroid/os/IBinder;Ljava/lang/String;ILjava/lang/String;)V
+Landroid/net/IConnectivityManager;->startTethering(ILandroid/os/ResultReceiver;ZLjava/lang/String;)V
+Landroid/net/IConnectivityManager;->stopKeepalive(Landroid/net/Network;I)V
+Landroid/net/IConnectivityManager;->stopTethering(ILjava/lang/String;)V
+Landroid/net/IConnectivityManager;->tether(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/net/IConnectivityManager;->unregisterNetworkFactory(Landroid/os/Messenger;)V
+Landroid/net/IConnectivityManager;->untether(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/net/IConnectivityManager;->updateLockdownVpn()Z
+Landroid/net/IpConfiguration$IpAssignment;->DHCP:Landroid/net/IpConfiguration$IpAssignment;
+Landroid/net/IpConfiguration$IpAssignment;->UNASSIGNED:Landroid/net/IpConfiguration$IpAssignment;
+Landroid/net/IpConfiguration$IpAssignment;->valueOf(Ljava/lang/String;)Landroid/net/IpConfiguration$IpAssignment;
+Landroid/net/IpConfiguration$IpAssignment;->values()[Landroid/net/IpConfiguration$IpAssignment;
+Landroid/net/IpConfiguration$ProxySettings;->PAC:Landroid/net/IpConfiguration$ProxySettings;
+Landroid/net/IpConfiguration$ProxySettings;->STATIC:Landroid/net/IpConfiguration$ProxySettings;
+Landroid/net/IpConfiguration$ProxySettings;->UNASSIGNED:Landroid/net/IpConfiguration$ProxySettings;
+Landroid/net/IpConfiguration$ProxySettings;->valueOf(Ljava/lang/String;)Landroid/net/IpConfiguration$ProxySettings;
+Landroid/net/IpConfiguration$ProxySettings;->values()[Landroid/net/IpConfiguration$ProxySettings;
+Landroid/net/IpConfiguration;-><init>()V
+Landroid/net/IpConfiguration;-><init>(Landroid/net/IpConfiguration;)V
+Landroid/net/IpConfiguration;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/net/IpConfiguration;->getHttpProxy()Landroid/net/ProxyInfo;
+Landroid/net/IpConfiguration;->getIpAssignment()Landroid/net/IpConfiguration$IpAssignment;
+Landroid/net/IpConfiguration;->getProxySettings()Landroid/net/IpConfiguration$ProxySettings;
+Landroid/net/IpConfiguration;->getStaticIpConfiguration()Landroid/net/StaticIpConfiguration;
+Landroid/net/IpConfiguration;->init(Landroid/net/IpConfiguration$IpAssignment;Landroid/net/IpConfiguration$ProxySettings;Landroid/net/StaticIpConfiguration;Landroid/net/ProxyInfo;)V
+Landroid/net/IpConfiguration;->ipAssignment:Landroid/net/IpConfiguration$IpAssignment;
+Landroid/net/IpConfiguration;->proxySettings:Landroid/net/IpConfiguration$ProxySettings;
+Landroid/net/IpConfiguration;->setHttpProxy(Landroid/net/ProxyInfo;)V
+Landroid/net/IpConfiguration;->setIpAssignment(Landroid/net/IpConfiguration$IpAssignment;)V
+Landroid/net/IpConfiguration;->setProxySettings(Landroid/net/IpConfiguration$ProxySettings;)V
+Landroid/net/IpConfiguration;->setStaticIpConfiguration(Landroid/net/StaticIpConfiguration;)V
+Landroid/net/IpConfiguration;->staticIpConfiguration:Landroid/net/StaticIpConfiguration;
+Landroid/net/IpConfiguration;->TAG:Ljava/lang/String;
+Landroid/net/IpPrefix;-><init>(Ljava/lang/String;)V
+Landroid/net/IpPrefix;-><init>(Ljava/net/InetAddress;I)V
+Landroid/net/IpPrefix;-><init>([BI)V
+Landroid/net/IpPrefix;->address:[B
+Landroid/net/IpPrefix;->checkAndMaskAddressAndPrefixLength()V
+Landroid/net/IpPrefix;->containsPrefix(Landroid/net/IpPrefix;)Z
+Landroid/net/IpPrefix;->isIPv4()Z
+Landroid/net/IpPrefix;->isIPv6()Z
+Landroid/net/IpPrefix;->lengthComparator()Ljava/util/Comparator;
+Landroid/net/IpPrefix;->prefixLength:I
+Landroid/net/KeepalivePacketData$InvalidPacketException;-><init>(I)V
+Landroid/net/KeepalivePacketData$InvalidPacketException;->error:I
+Landroid/net/KeepalivePacketData;-><init>(Landroid/os/Parcel;)V
+Landroid/net/KeepalivePacketData;-><init>(Ljava/net/InetAddress;ILjava/net/InetAddress;I[B)V
+Landroid/net/KeepalivePacketData;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/net/KeepalivePacketData;->dstAddress:Ljava/net/InetAddress;
+Landroid/net/KeepalivePacketData;->dstPort:I
+Landroid/net/KeepalivePacketData;->getPacket()[B
+Landroid/net/KeepalivePacketData;->IPV4_HEADER_LENGTH:I
+Landroid/net/KeepalivePacketData;->mPacket:[B
+Landroid/net/KeepalivePacketData;->nattKeepalivePacket(Ljava/net/InetAddress;ILjava/net/InetAddress;I)Landroid/net/KeepalivePacketData;
+Landroid/net/KeepalivePacketData;->srcAddress:Ljava/net/InetAddress;
+Landroid/net/KeepalivePacketData;->srcPort:I
+Landroid/net/KeepalivePacketData;->TAG:Ljava/lang/String;
+Landroid/net/KeepalivePacketData;->UDP_HEADER_LENGTH:I
+Landroid/net/LinkAddress;-><init>(Ljava/lang/String;II)V
+Landroid/net/LinkAddress;-><init>(Ljava/net/InetAddress;III)V
+Landroid/net/LinkAddress;-><init>(Ljava/net/InterfaceAddress;)V
+Landroid/net/LinkAddress;->flags:I
+Landroid/net/LinkAddress;->init(Ljava/net/InetAddress;III)V
+Landroid/net/LinkAddress;->isGlobalPreferred()Z
+Landroid/net/LinkAddress;->isIPv4()Z
+Landroid/net/LinkAddress;->isIPv6ULA()Z
+Landroid/net/LinkAddress;->scope:I
+Landroid/net/LinkAddress;->scopeForUnicastAddress(Ljava/net/InetAddress;)I
+Landroid/net/LinkProperties$CompareResult;-><init>()V
+Landroid/net/LinkProperties$CompareResult;-><init>(Ljava/util/Collection;Ljava/util/Collection;)V
+Landroid/net/LinkProperties$CompareResult;->added:Ljava/util/List;
+Landroid/net/LinkProperties$CompareResult;->removed:Ljava/util/List;
+Landroid/net/LinkProperties$ProvisioningChange;->valueOf(Ljava/lang/String;)Landroid/net/LinkProperties$ProvisioningChange;
+Landroid/net/LinkProperties;->addValidatedPrivateDnsServer(Ljava/net/InetAddress;)Z
+Landroid/net/LinkProperties;->compareAddresses(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult;
+Landroid/net/LinkProperties;->compareAllInterfaceNames(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult;
+Landroid/net/LinkProperties;->compareAllRoutes(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult;
+Landroid/net/LinkProperties;->compareDnses(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult;
+Landroid/net/LinkProperties;->compareValidatedPrivateDnses(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult;
+Landroid/net/LinkProperties;->ensureDirectlyConnectedRoutes()V
+Landroid/net/LinkProperties;->findLinkAddressIndex(Landroid/net/LinkAddress;)I
+Landroid/net/LinkProperties;->getValidatedPrivateDnsServers()Ljava/util/List;
+Landroid/net/LinkProperties;->hasIPv4AddressOnInterface(Ljava/lang/String;)Z
+Landroid/net/LinkProperties;->isIdenticalMtu(Landroid/net/LinkProperties;)Z
+Landroid/net/LinkProperties;->isIdenticalPrivateDns(Landroid/net/LinkProperties;)Z
+Landroid/net/LinkProperties;->isIdenticalTcpBufferSizes(Landroid/net/LinkProperties;)Z
+Landroid/net/LinkProperties;->isIdenticalValidatedPrivateDnses(Landroid/net/LinkProperties;)Z
+Landroid/net/LinkProperties;->isIPv4Provisioned()Z
+Landroid/net/LinkProperties;->isValidMtu(IZ)Z
+Landroid/net/LinkProperties;->MAX_MTU:I
+Landroid/net/LinkProperties;->mDnses:Ljava/util/ArrayList;
+Landroid/net/LinkProperties;->mDomains:Ljava/lang/String;
+Landroid/net/LinkProperties;->mHttpProxy:Landroid/net/ProxyInfo;
+Landroid/net/LinkProperties;->MIN_MTU:I
+Landroid/net/LinkProperties;->MIN_MTU_V6:I
+Landroid/net/LinkProperties;->mLinkAddresses:Ljava/util/ArrayList;
+Landroid/net/LinkProperties;->mMtu:I
+Landroid/net/LinkProperties;->mPrivateDnsServerName:Ljava/lang/String;
+Landroid/net/LinkProperties;->mRoutes:Ljava/util/ArrayList;
+Landroid/net/LinkProperties;->mStackedLinks:Ljava/util/Hashtable;
+Landroid/net/LinkProperties;->mTcpBufferSizes:Ljava/lang/String;
+Landroid/net/LinkProperties;->mUsePrivateDns:Z
+Landroid/net/LinkProperties;->mValidatedPrivateDnses:Ljava/util/ArrayList;
+Landroid/net/LinkProperties;->removeLinkAddress(Landroid/net/LinkAddress;)Z
+Landroid/net/LinkProperties;->removeStackedLink(Ljava/lang/String;)Z
+Landroid/net/LinkProperties;->removeValidatedPrivateDnsServer(Ljava/net/InetAddress;)Z
+Landroid/net/LinkProperties;->routeWithInterface(Landroid/net/RouteInfo;)Landroid/net/RouteInfo;
+Landroid/net/LinkProperties;->setPrivateDnsServerName(Ljava/lang/String;)V
+Landroid/net/LinkProperties;->setUsePrivateDns(Z)V
+Landroid/net/LinkProperties;->setValidatedPrivateDnsServers(Ljava/util/Collection;)V
+Landroid/net/MacAddress;-><init>(J)V
+Landroid/net/MacAddress;->BASE_GOOGLE_MAC:Landroid/net/MacAddress;
+Landroid/net/MacAddress;->byteAddrFromLongAddr(J)[B
+Landroid/net/MacAddress;->byteAddrFromStringAddr(Ljava/lang/String;)[B
+Landroid/net/MacAddress;->createRandomUnicastAddress()Landroid/net/MacAddress;
+Landroid/net/MacAddress;->createRandomUnicastAddress(Landroid/net/MacAddress;Ljava/util/Random;)Landroid/net/MacAddress;
+Landroid/net/MacAddress;->createRandomUnicastAddressWithGoogleBase()Landroid/net/MacAddress;
+Landroid/net/MacAddress;->ETHER_ADDR_BROADCAST:[B
+Landroid/net/MacAddress;->ETHER_ADDR_LEN:I
+Landroid/net/MacAddress;->isMacAddress([B)Z
+Landroid/net/MacAddress;->isMulticastAddress()Z
+Landroid/net/MacAddress;->LOCALLY_ASSIGNED_MASK:J
+Landroid/net/MacAddress;->longAddrFromByteAddr([B)J
+Landroid/net/MacAddress;->longAddrFromStringAddr(Ljava/lang/String;)J
+Landroid/net/MacAddress;->macAddressType([B)I
+Landroid/net/MacAddress;->mAddr:J
+Landroid/net/MacAddress;->MULTICAST_MASK:J
+Landroid/net/MacAddress;->NIC_MASK:J
+Landroid/net/MacAddress;->OUI_MASK:J
+Landroid/net/MacAddress;->stringAddrFromByteAddr([B)Ljava/lang/String;
+Landroid/net/MacAddress;->stringAddrFromLongAddr(J)Ljava/lang/String;
+Landroid/net/MacAddress;->TYPE_UNKNOWN:I
+Landroid/net/MacAddress;->VALID_LONG_MASK:J
+Landroid/net/Network$NetworkBoundSocketFactory;->connectToHost(Ljava/lang/String;ILjava/net/SocketAddress;)Ljava/net/Socket;
+Landroid/net/Network$NetworkBoundSocketFactory;->mNetId:I
+Landroid/net/Network;-><init>(Landroid/net/Network;)V
+Landroid/net/Network;->getNetIdForResolv()I
+Landroid/net/Network;->HANDLE_MAGIC:J
+Landroid/net/Network;->HANDLE_MAGIC_SIZE:I
+Landroid/net/Network;->httpKeepAlive:Z
+Landroid/net/Network;->httpKeepAliveDurationMs:J
+Landroid/net/Network;->httpMaxConnections:I
+Landroid/net/Network;->maybeInitUrlConnectionFactory()V
+Landroid/net/Network;->mLock:Ljava/lang/Object;
+Landroid/net/Network;->mNetworkBoundSocketFactory:Landroid/net/Network$NetworkBoundSocketFactory;
+Landroid/net/Network;->mPrivateDnsBypass:Z
+Landroid/net/Network;->mUrlConnectionFactory:Lcom/android/okhttp/internalandroidapi/HttpURLConnectionFactory;
+Landroid/net/Network;->setPrivateDnsBypass(Z)V
+Landroid/net/Network;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+Landroid/net/NetworkAgent;-><init>(Landroid/os/Looper;Landroid/content/Context;Ljava/lang/String;Landroid/net/NetworkInfo;Landroid/net/NetworkCapabilities;Landroid/net/LinkProperties;I)V
+Landroid/net/NetworkAgent;-><init>(Landroid/os/Looper;Landroid/content/Context;Ljava/lang/String;Landroid/net/NetworkInfo;Landroid/net/NetworkCapabilities;Landroid/net/LinkProperties;ILandroid/net/NetworkMisc;)V
+Landroid/net/NetworkAgent;->BASE:I
+Landroid/net/NetworkAgent;->BW_REFRESH_MIN_WIN_MS:J
+Landroid/net/NetworkAgent;->CMD_PREVENT_AUTOMATIC_RECONNECT:I
+Landroid/net/NetworkAgent;->CMD_REPORT_NETWORK_STATUS:I
+Landroid/net/NetworkAgent;->CMD_REQUEST_BANDWIDTH_UPDATE:I
+Landroid/net/NetworkAgent;->CMD_SAVE_ACCEPT_UNVALIDATED:I
+Landroid/net/NetworkAgent;->CMD_SET_SIGNAL_STRENGTH_THRESHOLDS:I
+Landroid/net/NetworkAgent;->CMD_START_PACKET_KEEPALIVE:I
+Landroid/net/NetworkAgent;->CMD_STOP_PACKET_KEEPALIVE:I
+Landroid/net/NetworkAgent;->CMD_SUSPECT_BAD:I
+Landroid/net/NetworkAgent;->DBG:Z
+Landroid/net/NetworkAgent;->EVENT_NETWORK_CAPABILITIES_CHANGED:I
+Landroid/net/NetworkAgent;->EVENT_NETWORK_INFO_CHANGED:I
+Landroid/net/NetworkAgent;->EVENT_NETWORK_PROPERTIES_CHANGED:I
+Landroid/net/NetworkAgent;->EVENT_NETWORK_SCORE_CHANGED:I
+Landroid/net/NetworkAgent;->EVENT_PACKET_KEEPALIVE:I
+Landroid/net/NetworkAgent;->EVENT_SET_EXPLICITLY_SELECTED:I
+Landroid/net/NetworkAgent;->explicitlySelected(Z)V
+Landroid/net/NetworkAgent;->INVALID_NETWORK:I
+Landroid/net/NetworkAgent;->log(Ljava/lang/String;)V
+Landroid/net/NetworkAgent;->LOG_TAG:Ljava/lang/String;
+Landroid/net/NetworkAgent;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel;
+Landroid/net/NetworkAgent;->mContext:Landroid/content/Context;
+Landroid/net/NetworkAgent;->mLastBwRefreshTime:J
+Landroid/net/NetworkAgent;->mPollLcePending:Ljava/util/concurrent/atomic/AtomicBoolean;
+Landroid/net/NetworkAgent;->mPollLceScheduled:Z
+Landroid/net/NetworkAgent;->mPreConnectedQueue:Ljava/util/ArrayList;
+Landroid/net/NetworkAgent;->netId:I
+Landroid/net/NetworkAgent;->networkStatus(ILjava/lang/String;)V
+Landroid/net/NetworkAgent;->onPacketKeepaliveEvent(II)V
+Landroid/net/NetworkAgent;->pollLceData()V
+Landroid/net/NetworkAgent;->preventAutomaticReconnect()V
+Landroid/net/NetworkAgent;->queueOrSendMessage(III)V
+Landroid/net/NetworkAgent;->queueOrSendMessage(IIILjava/lang/Object;)V
+Landroid/net/NetworkAgent;->queueOrSendMessage(ILjava/lang/Object;)V
+Landroid/net/NetworkAgent;->queueOrSendMessage(Landroid/os/Message;)V
+Landroid/net/NetworkAgent;->REDIRECT_URL_KEY:Ljava/lang/String;
+Landroid/net/NetworkAgent;->saveAcceptUnvalidated(Z)V
+Landroid/net/NetworkAgent;->sendLinkProperties(Landroid/net/LinkProperties;)V
+Landroid/net/NetworkAgent;->sendNetworkCapabilities(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkAgent;->sendNetworkScore(I)V
+Landroid/net/NetworkAgent;->setSignalStrengthThresholds([I)V
+Landroid/net/NetworkAgent;->startPacketKeepalive(Landroid/os/Message;)V
+Landroid/net/NetworkAgent;->stopPacketKeepalive(Landroid/os/Message;)V
+Landroid/net/NetworkAgent;->unwanted()V
+Landroid/net/NetworkAgent;->VALID_NETWORK:I
+Landroid/net/NetworkAgent;->VDBG:Z
+Landroid/net/NetworkAgent;->WIFI_BASE_SCORE:I
+Landroid/net/NetworkBadging;-><init>()V
+Landroid/net/NetworkBadging;->getBadgedWifiSignalResource(I)I
+Landroid/net/NetworkBadging;->getWifiSignalResource(I)I
+Landroid/net/NetworkCapabilities$NameOf;->nameOf(I)Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->addUnwantedCapability(I)V
+Landroid/net/NetworkCapabilities;->appendStringRepresentationOfBitMaskToStringBuilder(Ljava/lang/StringBuilder;JLandroid/net/NetworkCapabilities$NameOf;Ljava/lang/String;)V
+Landroid/net/NetworkCapabilities;->appliesToUid(I)Z
+Landroid/net/NetworkCapabilities;->appliesToUidRange(Landroid/net/UidRange;)Z
+Landroid/net/NetworkCapabilities;->capabilityNameOf(I)Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->capabilityNamesOf([I)Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->checkValidCapability(I)V
+Landroid/net/NetworkCapabilities;->checkValidTransportType(I)V
+Landroid/net/NetworkCapabilities;->clearAll()V
+Landroid/net/NetworkCapabilities;->combineCapabilities(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->combineLinkBandwidths(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->combineNetCapabilities(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->combineSignalStrength(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->combineSpecifiers(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->combineSSIDs(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->combineTransportTypes(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->combineUids(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->DEFAULT_CAPABILITIES:J
+Landroid/net/NetworkCapabilities;->describeFirstNonRequestableCapability()Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->describeImmutableDifferences(Landroid/net/NetworkCapabilities;)Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->equalRequestableCapabilities(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->equalsLinkBandwidths(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->equalsNetCapabilities(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->equalsNetCapabilitiesRequestable(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->equalsSignalStrength(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->equalsSpecifier(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->equalsSSID(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->equalsTransportTypes(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->equalsUids(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->FORCE_RESTRICTED_CAPABILITIES:J
+Landroid/net/NetworkCapabilities;->getSSID()Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->getUids()Ljava/util/Set;
+Landroid/net/NetworkCapabilities;->getUnwantedCapabilities()[I
+Landroid/net/NetworkCapabilities;->hasUnwantedCapability(I)Z
+Landroid/net/NetworkCapabilities;->INVALID_UID:I
+Landroid/net/NetworkCapabilities;->isValidCapability(I)Z
+Landroid/net/NetworkCapabilities;->isValidTransport(I)Z
+Landroid/net/NetworkCapabilities;->LINK_BANDWIDTH_UNSPECIFIED:I
+Landroid/net/NetworkCapabilities;->maxBandwidth(II)I
+Landroid/net/NetworkCapabilities;->MAX_NET_CAPABILITY:I
+Landroid/net/NetworkCapabilities;->MAX_TRANSPORT:I
+Landroid/net/NetworkCapabilities;->maybeMarkCapabilitiesRestricted()V
+Landroid/net/NetworkCapabilities;->mEstablishingVpnAppUid:I
+Landroid/net/NetworkCapabilities;->minBandwidth(II)I
+Landroid/net/NetworkCapabilities;->MIN_NET_CAPABILITY:I
+Landroid/net/NetworkCapabilities;->MIN_TRANSPORT:I
+Landroid/net/NetworkCapabilities;->mLinkDownBandwidthKbps:I
+Landroid/net/NetworkCapabilities;->mLinkUpBandwidthKbps:I
+Landroid/net/NetworkCapabilities;->mNetworkSpecifier:Landroid/net/NetworkSpecifier;
+Landroid/net/NetworkCapabilities;->mSSID:Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->mTransportTypes:J
+Landroid/net/NetworkCapabilities;->mUids:Landroid/util/ArraySet;
+Landroid/net/NetworkCapabilities;->mUnwantedNetworkCapabilities:J
+Landroid/net/NetworkCapabilities;->MUTABLE_CAPABILITIES:J
+Landroid/net/NetworkCapabilities;->NON_REQUESTABLE_CAPABILITIES:J
+Landroid/net/NetworkCapabilities;->removeTransportType(I)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->RESTRICTED_CAPABILITIES:J
+Landroid/net/NetworkCapabilities;->satisfiedByImmutableNetworkCapabilities(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->satisfiedByLinkBandwidths(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->satisfiedByNetCapabilities(Landroid/net/NetworkCapabilities;Z)Z
+Landroid/net/NetworkCapabilities;->satisfiedByNetworkCapabilities(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->satisfiedByNetworkCapabilities(Landroid/net/NetworkCapabilities;Z)Z
+Landroid/net/NetworkCapabilities;->satisfiedBySignalStrength(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->satisfiedBySpecifier(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->satisfiedBySSID(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->satisfiedByTransportTypes(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->satisfiedByUids(Landroid/net/NetworkCapabilities;)Z
+Landroid/net/NetworkCapabilities;->set(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkCapabilities;->setCapabilities([I)V
+Landroid/net/NetworkCapabilities;->setCapabilities([I[I)V
+Landroid/net/NetworkCapabilities;->setCapability(IZ)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->setEstablishingVpnAppUid(I)V
+Landroid/net/NetworkCapabilities;->setLinkDownstreamBandwidthKbps(I)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->setLinkUpstreamBandwidthKbps(I)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->setNetworkSpecifier(Landroid/net/NetworkSpecifier;)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->setSingleUid(I)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->setSSID(Ljava/lang/String;)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->setTransportType(IZ)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->setTransportTypes([I)V
+Landroid/net/NetworkCapabilities;->setUids(Ljava/util/Set;)Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkCapabilities;->SIGNAL_STRENGTH_UNSPECIFIED:I
+Landroid/net/NetworkCapabilities;->TAG:Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->transportNameOf(I)Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->TRANSPORT_NAMES:[Ljava/lang/String;
+Landroid/net/NetworkCapabilities;->UNRESTRICTED_CAPABILITIES:J
+Landroid/net/NetworkCapabilities;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+Landroid/net/NetworkCapabilitiesProto;-><init>()V
+Landroid/net/NetworkCapabilitiesProto;->CAN_REPORT_SIGNAL_STRENGTH:J
+Landroid/net/NetworkCapabilitiesProto;->CAPABILITIES:J
+Landroid/net/NetworkCapabilitiesProto;->LINK_DOWN_BANDWIDTH_KBPS:J
+Landroid/net/NetworkCapabilitiesProto;->LINK_UP_BANDWIDTH_KBPS:J
+Landroid/net/NetworkCapabilitiesProto;->NETWORK_SPECIFIER:J
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_CAPTIVE_PORTAL:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_CBS:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_DUN:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_EIMS:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_FOREGROUND:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_FOTA:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_IA:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_IMS:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_INTERNET:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_MMS:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_NOT_METERED:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_NOT_RESTRICTED:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_NOT_ROAMING:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_NOT_VPN:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_RCS:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_SUPL:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_TRUSTED:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_VALIDATED:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_WIFI_P2P:I
+Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_XCAP:I
+Landroid/net/NetworkCapabilitiesProto;->SIGNAL_STRENGTH:J
+Landroid/net/NetworkCapabilitiesProto;->TRANSPORTS:J
+Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_BLUETOOTH:I
+Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_CELLULAR:I
+Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_ETHERNET:I
+Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_LOWPAN:I
+Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_VPN:I
+Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_WIFI:I
+Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_WIFI_AWARE:I
+Landroid/net/NetworkConfig;-><init>(Ljava/lang/String;)V
+Landroid/net/NetworkConfig;->dependencyMet:Z
+Landroid/net/NetworkConfig;->isDefault()Z
+Landroid/net/NetworkConfig;->name:Ljava/lang/String;
+Landroid/net/NetworkConfig;->priority:I
+Landroid/net/NetworkConfig;->radio:I
+Landroid/net/NetworkConfig;->restoreTime:I
+Landroid/net/NetworkConfig;->type:I
+Landroid/net/NetworkFactory$NetworkRequestInfo;->request:Landroid/net/NetworkRequest;
+Landroid/net/NetworkFactory$NetworkRequestInfo;->requested:Z
+Landroid/net/NetworkFactory$NetworkRequestInfo;->score:I
+Landroid/net/NetworkFactory;->acceptRequest(Landroid/net/NetworkRequest;I)Z
+Landroid/net/NetworkFactory;->addNetworkRequest(Landroid/net/NetworkRequest;I)V
+Landroid/net/NetworkFactory;->BASE:I
+Landroid/net/NetworkFactory;->CMD_CANCEL_REQUEST:I
+Landroid/net/NetworkFactory;->CMD_REQUEST_NETWORK:I
+Landroid/net/NetworkFactory;->CMD_SET_FILTER:I
+Landroid/net/NetworkFactory;->CMD_SET_SCORE:I
+Landroid/net/NetworkFactory;->DBG:Z
+Landroid/net/NetworkFactory;->evalRequest(Landroid/net/NetworkFactory$NetworkRequestInfo;)V
+Landroid/net/NetworkFactory;->evalRequests()V
+Landroid/net/NetworkFactory;->getRequestCount()I
+Landroid/net/NetworkFactory;->handleAddRequest(Landroid/net/NetworkRequest;I)V
+Landroid/net/NetworkFactory;->handleRemoveRequest(Landroid/net/NetworkRequest;)V
+Landroid/net/NetworkFactory;->handleSetFilter(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkFactory;->handleSetScore(I)V
+Landroid/net/NetworkFactory;->log(Ljava/lang/String;)V
+Landroid/net/NetworkFactory;->LOG_TAG:Ljava/lang/String;
+Landroid/net/NetworkFactory;->mCapabilityFilter:Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkFactory;->mContext:Landroid/content/Context;
+Landroid/net/NetworkFactory;->mMessenger:Landroid/os/Messenger;
+Landroid/net/NetworkFactory;->mNetworkRequests:Landroid/util/SparseArray;
+Landroid/net/NetworkFactory;->mRefCount:I
+Landroid/net/NetworkFactory;->mScore:I
+Landroid/net/NetworkFactory;->needNetworkFor(Landroid/net/NetworkRequest;I)V
+Landroid/net/NetworkFactory;->reevaluateAllRequests()V
+Landroid/net/NetworkFactory;->register()V
+Landroid/net/NetworkFactory;->releaseNetworkFor(Landroid/net/NetworkRequest;)V
+Landroid/net/NetworkFactory;->removeNetworkRequest(Landroid/net/NetworkRequest;)V
+Landroid/net/NetworkFactory;->setCapabilityFilter(Landroid/net/NetworkCapabilities;)V
+Landroid/net/NetworkFactory;->startNetwork()V
+Landroid/net/NetworkFactory;->stopNetwork()V
+Landroid/net/NetworkFactory;->unregister()V
+Landroid/net/NetworkFactory;->VDBG:Z
+Landroid/net/NetworkIdentity;-><init>(IILjava/lang/String;Ljava/lang/String;ZZZ)V
+Landroid/net/NetworkIdentity;->buildNetworkIdentity(Landroid/content/Context;Landroid/net/NetworkState;Z)Landroid/net/NetworkIdentity;
+Landroid/net/NetworkIdentity;->COMBINE_SUBTYPE_ENABLED:Z
+Landroid/net/NetworkIdentity;->compareTo(Landroid/net/NetworkIdentity;)I
+Landroid/net/NetworkIdentity;->getDefaultNetwork()Z
+Landroid/net/NetworkIdentity;->getMetered()Z
+Landroid/net/NetworkIdentity;->getNetworkId()Ljava/lang/String;
+Landroid/net/NetworkIdentity;->getRoaming()Z
+Landroid/net/NetworkIdentity;->getSubscriberId()Ljava/lang/String;
+Landroid/net/NetworkIdentity;->getSubType()I
+Landroid/net/NetworkIdentity;->getType()I
+Landroid/net/NetworkIdentity;->mDefaultNetwork:Z
+Landroid/net/NetworkIdentity;->mMetered:Z
+Landroid/net/NetworkIdentity;->mNetworkId:Ljava/lang/String;
+Landroid/net/NetworkIdentity;->mRoaming:Z
+Landroid/net/NetworkIdentity;->mSubscriberId:Ljava/lang/String;
+Landroid/net/NetworkIdentity;->mSubType:I
+Landroid/net/NetworkIdentity;->mType:I
+Landroid/net/NetworkIdentity;->scrubSubscriberId(Ljava/lang/String;)Ljava/lang/String;
+Landroid/net/NetworkIdentity;->scrubSubscriberId([Ljava/lang/String;)[Ljava/lang/String;
+Landroid/net/NetworkIdentity;->SUBTYPE_COMBINED:I
+Landroid/net/NetworkIdentity;->TAG:Ljava/lang/String;
+Landroid/net/NetworkIdentity;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+Landroid/net/NetworkInfo;->mDetailedState:Landroid/net/NetworkInfo$DetailedState;
+Landroid/net/NetworkInfo;->mExtraInfo:Ljava/lang/String;
+Landroid/net/NetworkInfo;->mIsAvailable:Z
+Landroid/net/NetworkInfo;->mIsFailover:Z
+Landroid/net/NetworkInfo;->mIsRoaming:Z
+Landroid/net/NetworkInfo;->mNetworkType:I
+Landroid/net/NetworkInfo;->mReason:Ljava/lang/String;
+Landroid/net/NetworkInfo;->mState:Landroid/net/NetworkInfo$State;
+Landroid/net/NetworkInfo;->mSubtype:I
+Landroid/net/NetworkInfo;->mSubtypeName:Ljava/lang/String;
+Landroid/net/NetworkInfo;->mTypeName:Ljava/lang/String;
+Landroid/net/NetworkInfo;->setExtraInfo(Ljava/lang/String;)V
+Landroid/net/NetworkInfo;->setType(I)V
+Landroid/net/NetworkInfo;->stateMap:Ljava/util/EnumMap;
+Landroid/net/NetworkKey;-><init>(Landroid/os/Parcel;)V
+Landroid/net/NetworkKey;->createFromScanResult(Landroid/net/wifi/ScanResult;)Landroid/net/NetworkKey;
+Landroid/net/NetworkKey;->createFromWifiInfo(Landroid/net/wifi/WifiInfo;)Landroid/net/NetworkKey;
+Landroid/net/NetworkKey;->TAG:Ljava/lang/String;
+Landroid/net/NetworkMisc;-><init>()V
+Landroid/net/NetworkMisc;-><init>(Landroid/net/NetworkMisc;)V
+Landroid/net/NetworkMisc;->acceptUnvalidated:Z
+Landroid/net/NetworkMisc;->allowBypass:Z
+Landroid/net/NetworkMisc;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/net/NetworkMisc;->explicitlySelected:Z
+Landroid/net/NetworkMisc;->provisioningNotificationDisabled:Z
+Landroid/net/NetworkMisc;->subscriberId:Ljava/lang/String;
+Landroid/net/NetworkPolicy;-><init>(Landroid/net/NetworkTemplate;ILjava/lang/String;JJZ)V
+Landroid/net/NetworkPolicy;-><init>(Landroid/net/NetworkTemplate;Landroid/util/RecurrenceRule;JJJJJZZ)V
+Landroid/net/NetworkPolicy;-><init>(Landroid/net/NetworkTemplate;Landroid/util/RecurrenceRule;JJJJZZ)V
+Landroid/net/NetworkPolicy;-><init>(Landroid/os/Parcel;)V
+Landroid/net/NetworkPolicy;->buildRule(ILjava/time/ZoneId;)Landroid/util/RecurrenceRule;
+Landroid/net/NetworkPolicy;->cycleIterator()Ljava/util/Iterator;
+Landroid/net/NetworkPolicy;->cycleRule:Landroid/util/RecurrenceRule;
+Landroid/net/NetworkPolicy;->CYCLE_NONE:I
+Landroid/net/NetworkPolicy;->DEFAULT_MTU:J
+Landroid/net/NetworkPolicy;->getBytesForBackup()[B
+Landroid/net/NetworkPolicy;->getNetworkPolicyFromBackup(Ljava/io/DataInputStream;)Landroid/net/NetworkPolicy;
+Landroid/net/NetworkPolicy;->hasCycle()Z
+Landroid/net/NetworkPolicy;->lastLimitSnooze:J
+Landroid/net/NetworkPolicy;->lastRapidSnooze:J
+Landroid/net/NetworkPolicy;->lastWarningSnooze:J
+Landroid/net/NetworkPolicy;->LIMIT_DISABLED:J
+Landroid/net/NetworkPolicy;->SNOOZE_NEVER:J
+Landroid/net/NetworkPolicy;->VERSION_INIT:I
+Landroid/net/NetworkPolicy;->VERSION_RAPID:I
+Landroid/net/NetworkPolicy;->VERSION_RULE:I
+Landroid/net/NetworkPolicy;->WARNING_DISABLED:J
+Landroid/net/NetworkPolicyManager$Listener;-><init>()V
+Landroid/net/NetworkPolicyManager$Listener;->onMeteredIfacesChanged([Ljava/lang/String;)V
+Landroid/net/NetworkPolicyManager$Listener;->onRestrictBackgroundChanged(Z)V
+Landroid/net/NetworkPolicyManager$Listener;->onSubscriptionOverride(III)V
+Landroid/net/NetworkPolicyManager$Listener;->onUidPoliciesChanged(II)V
+Landroid/net/NetworkPolicyManager$Listener;->onUidRulesChanged(II)V
+Landroid/net/NetworkPolicyManager;-><init>(Landroid/content/Context;Landroid/net/INetworkPolicyManager;)V
+Landroid/net/NetworkPolicyManager;->addUidPolicy(II)V
+Landroid/net/NetworkPolicyManager;->ALLOW_PLATFORM_APP_POLICY:Z
+Landroid/net/NetworkPolicyManager;->cycleIterator(Landroid/net/NetworkPolicy;)Ljava/util/Iterator;
+Landroid/net/NetworkPolicyManager;->EXTRA_NETWORK_TEMPLATE:Ljava/lang/String;
+Landroid/net/NetworkPolicyManager;->factoryReset(Ljava/lang/String;)V
+Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_DOZABLE:I
+Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NAME_DOZABLE:Ljava/lang/String;
+Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NAME_NONE:Ljava/lang/String;
+Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NAME_POWERSAVE:Ljava/lang/String;
+Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NAME_STANDBY:Ljava/lang/String;
+Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NONE:I
+Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_POWERSAVE:I
+Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_STANDBY:I
+Landroid/net/NetworkPolicyManager;->FIREWALL_RULE_ALLOW:I
+Landroid/net/NetworkPolicyManager;->FIREWALL_RULE_DEFAULT:I
+Landroid/net/NetworkPolicyManager;->FIREWALL_RULE_DENY:I
+Landroid/net/NetworkPolicyManager;->FIREWALL_TYPE_BLACKLIST:I
+Landroid/net/NetworkPolicyManager;->FIREWALL_TYPE_WHITELIST:I
+Landroid/net/NetworkPolicyManager;->FOREGROUND_THRESHOLD_STATE:I
+Landroid/net/NetworkPolicyManager;->isProcStateAllowedWhileIdleOrPowerSaveMode(I)Z
+Landroid/net/NetworkPolicyManager;->isProcStateAllowedWhileOnRestrictBackground(I)Z
+Landroid/net/NetworkPolicyManager;->isUidValidForPolicy(Landroid/content/Context;I)Z
+Landroid/net/NetworkPolicyManager;->MASK_ALL_NETWORKS:I
+Landroid/net/NetworkPolicyManager;->MASK_METERED_NETWORKS:I
+Landroid/net/NetworkPolicyManager;->mContext:Landroid/content/Context;
+Landroid/net/NetworkPolicyManager;->OVERRIDE_CONGESTED:I
+Landroid/net/NetworkPolicyManager;->OVERRIDE_UNMETERED:I
+Landroid/net/NetworkPolicyManager;->POLICY_ALLOW_METERED_BACKGROUND:I
+Landroid/net/NetworkPolicyManager;->POLICY_NONE:I
+Landroid/net/NetworkPolicyManager;->POLICY_REJECT_METERED_BACKGROUND:I
+Landroid/net/NetworkPolicyManager;->removeUidPolicy(II)V
+Landroid/net/NetworkPolicyManager;->resolveNetworkId(Landroid/net/wifi/WifiConfiguration;)Ljava/lang/String;
+Landroid/net/NetworkPolicyManager;->resolveNetworkId(Ljava/lang/String;)Ljava/lang/String;
+Landroid/net/NetworkPolicyManager;->RULE_ALLOW_ALL:I
+Landroid/net/NetworkPolicyManager;->RULE_ALLOW_METERED:I
+Landroid/net/NetworkPolicyManager;->RULE_NONE:I
+Landroid/net/NetworkPolicyManager;->RULE_REJECT_ALL:I
+Landroid/net/NetworkPolicyManager;->RULE_REJECT_METERED:I
+Landroid/net/NetworkPolicyManager;->RULE_TEMPORARY_ALLOW_METERED:I
+Landroid/net/NetworkPolicyManager;->setNetworkPolicies([Landroid/net/NetworkPolicy;)V
+Landroid/net/NetworkPolicyManager;->uidPoliciesToString(I)Ljava/lang/String;
+Landroid/net/NetworkPolicyManager;->uidRulesToString(I)Ljava/lang/String;
+Landroid/net/NetworkProto;-><init>()V
+Landroid/net/NetworkProto;->NET_ID:J
+Landroid/net/NetworkQuotaInfo;-><init>()V
+Landroid/net/NetworkQuotaInfo;-><init>(Landroid/os/Parcel;)V
+Landroid/net/NetworkQuotaInfo;->NO_LIMIT:J
+Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->enforceCallingPermission()V
+Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->execute(Ljava/lang/Runnable;)V
+Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->mContext:Landroid/content/Context;
+Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->mExecutor:Ljava/util/concurrent/Executor;
+Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->mHandler:Landroid/os/Handler;
+Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->requestScores([Landroid/net/NetworkKey;)V
+Landroid/net/NetworkRecommendationProvider;->mService:Landroid/os/IBinder;
+Landroid/net/NetworkRecommendationProvider;->TAG:Ljava/lang/String;
+Landroid/net/NetworkRecommendationProvider;->VERBOSE:Z
+Landroid/net/NetworkRequest$Builder;->addUnwantedCapability(I)Landroid/net/NetworkRequest$Builder;
+Landroid/net/NetworkRequest$Builder;->mNetworkCapabilities:Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkRequest$Builder;->setCapabilities(Landroid/net/NetworkCapabilities;)Landroid/net/NetworkRequest$Builder;
+Landroid/net/NetworkRequest$Builder;->setLinkDownstreamBandwidthKbps(I)Landroid/net/NetworkRequest$Builder;
+Landroid/net/NetworkRequest$Builder;->setLinkUpstreamBandwidthKbps(I)Landroid/net/NetworkRequest$Builder;
+Landroid/net/NetworkRequest$Builder;->setUids(Ljava/util/Set;)Landroid/net/NetworkRequest$Builder;
+Landroid/net/NetworkRequest$Type;->BACKGROUND_REQUEST:Landroid/net/NetworkRequest$Type;
+Landroid/net/NetworkRequest$Type;->LISTEN:Landroid/net/NetworkRequest$Type;
+Landroid/net/NetworkRequest$Type;->NONE:Landroid/net/NetworkRequest$Type;
+Landroid/net/NetworkRequest$Type;->REQUEST:Landroid/net/NetworkRequest$Type;
+Landroid/net/NetworkRequest$Type;->TRACK_DEFAULT:Landroid/net/NetworkRequest$Type;
+Landroid/net/NetworkRequest$Type;->valueOf(Ljava/lang/String;)Landroid/net/NetworkRequest$Type;
+Landroid/net/NetworkRequest$Type;->values()[Landroid/net/NetworkRequest$Type;
+Landroid/net/NetworkRequest;-><init>(Landroid/net/NetworkCapabilities;IILandroid/net/NetworkRequest$Type;)V
+Landroid/net/NetworkRequest;-><init>(Landroid/net/NetworkRequest;)V
+Landroid/net/NetworkRequest;->hasUnwantedCapability(I)Z
+Landroid/net/NetworkRequest;->isBackgroundRequest()Z
+Landroid/net/NetworkRequest;->isForegroundRequest()Z
+Landroid/net/NetworkRequest;->isListen()Z
+Landroid/net/NetworkRequest;->isRequest()Z
+Landroid/net/NetworkRequest;->type:Landroid/net/NetworkRequest$Type;
+Landroid/net/NetworkRequest;->typeToProtoEnum(Landroid/net/NetworkRequest$Type;)I
+Landroid/net/NetworkRequest;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+Landroid/net/NetworkRequestProto;-><init>()V
+Landroid/net/NetworkRequestProto;->LEGACY_TYPE:J
+Landroid/net/NetworkRequestProto;->NETWORK_CAPABILITIES:J
+Landroid/net/NetworkRequestProto;->REQUEST_ID:J
+Landroid/net/NetworkRequestProto;->TYPE:J
+Landroid/net/NetworkRequestProto;->TYPE_BACKGROUND_REQUEST:I
+Landroid/net/NetworkRequestProto;->TYPE_LISTEN:I
+Landroid/net/NetworkRequestProto;->TYPE_NONE:I
+Landroid/net/NetworkRequestProto;->TYPE_REQUEST:I
+Landroid/net/NetworkRequestProto;->TYPE_TRACK_DEFAULT:I
+Landroid/net/NetworkRequestProto;->TYPE_UNKNOWN:I
+Landroid/net/NetworkScoreManager;-><init>(Landroid/content/Context;)V
+Landroid/net/NetworkScoreManager;->CACHE_FILTER_CURRENT_NETWORK:I
+Landroid/net/NetworkScoreManager;->CACHE_FILTER_NONE:I
+Landroid/net/NetworkScoreManager;->CACHE_FILTER_SCAN_RESULTS:I
+Landroid/net/NetworkScoreManager;->getActiveScorer()Landroid/net/NetworkScorerAppData;
+Landroid/net/NetworkScoreManager;->getAllValidScorers()Ljava/util/List;
+Landroid/net/NetworkScoreManager;->isCallerActiveScorer(I)Z
+Landroid/net/NetworkScoreManager;->mContext:Landroid/content/Context;
+Landroid/net/NetworkScoreManager;->mService:Landroid/net/INetworkScoreService;
+Landroid/net/NetworkScoreManager;->NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID_META_DATA:Ljava/lang/String;
+Landroid/net/NetworkScoreManager;->RECOMMENDATIONS_ENABLED_FORCED_OFF:I
+Landroid/net/NetworkScoreManager;->RECOMMENDATIONS_ENABLED_OFF:I
+Landroid/net/NetworkScoreManager;->RECOMMENDATIONS_ENABLED_ON:I
+Landroid/net/NetworkScoreManager;->RECOMMENDATION_SERVICE_LABEL_META_DATA:Ljava/lang/String;
+Landroid/net/NetworkScoreManager;->registerNetworkScoreCache(ILandroid/net/INetworkScoreCache;)V
+Landroid/net/NetworkScoreManager;->registerNetworkScoreCache(ILandroid/net/INetworkScoreCache;I)V
+Landroid/net/NetworkScoreManager;->requestScores([Landroid/net/NetworkKey;)Z
+Landroid/net/NetworkScoreManager;->unregisterNetworkScoreCache(ILandroid/net/INetworkScoreCache;)V
+Landroid/net/NetworkScoreManager;->USE_OPEN_WIFI_PACKAGE_META_DATA:Ljava/lang/String;
+Landroid/net/NetworkScorerAppData;-><init>(ILandroid/content/ComponentName;Ljava/lang/String;Landroid/content/ComponentName;Ljava/lang/String;)V
+Landroid/net/NetworkScorerAppData;-><init>(Landroid/os/Parcel;)V
+Landroid/net/NetworkScorerAppData;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/net/NetworkScorerAppData;->getEnableUseOpenWifiActivity()Landroid/content/ComponentName;
+Landroid/net/NetworkScorerAppData;->getNetworkAvailableNotificationChannelId()Ljava/lang/String;
+Landroid/net/NetworkScorerAppData;->getRecommendationServiceComponent()Landroid/content/ComponentName;
+Landroid/net/NetworkScorerAppData;->getRecommendationServiceLabel()Ljava/lang/String;
+Landroid/net/NetworkScorerAppData;->getRecommendationServicePackageName()Ljava/lang/String;
+Landroid/net/NetworkScorerAppData;->mEnableUseOpenWifiActivity:Landroid/content/ComponentName;
+Landroid/net/NetworkScorerAppData;->mNetworkAvailableNotificationChannelId:Ljava/lang/String;
+Landroid/net/NetworkScorerAppData;->mRecommendationService:Landroid/content/ComponentName;
+Landroid/net/NetworkScorerAppData;->mRecommendationServiceLabel:Ljava/lang/String;
+Landroid/net/NetworkScorerAppData;->packageUid:I
+Landroid/net/NetworkSpecifier;-><init>()V
+Landroid/net/NetworkSpecifier;->assertValidFromUid(I)V
+Landroid/net/NetworkSpecifier;->satisfiedBy(Landroid/net/NetworkSpecifier;)Z
+Landroid/net/NetworkState;-><init>(Landroid/net/NetworkInfo;Landroid/net/LinkProperties;Landroid/net/NetworkCapabilities;Landroid/net/Network;Ljava/lang/String;Ljava/lang/String;)V
+Landroid/net/NetworkState;->EMPTY:Landroid/net/NetworkState;
+Landroid/net/NetworkState;->linkProperties:Landroid/net/LinkProperties;
+Landroid/net/NetworkState;->networkCapabilities:Landroid/net/NetworkCapabilities;
+Landroid/net/NetworkState;->networkId:Ljava/lang/String;
+Landroid/net/NetworkState;->networkInfo:Landroid/net/NetworkInfo;
+Landroid/net/NetworkState;->SANITY_CHECK_ROAMING:Z
+Landroid/net/NetworkState;->subscriberId:Ljava/lang/String;
+Landroid/net/NetworkStats$Entry;-><init>(JJJJJ)V
+Landroid/net/NetworkStats$Entry;-><init>(Ljava/lang/String;IIIIIIJJJJJ)V
+Landroid/net/NetworkStats$Entry;-><init>(Ljava/lang/String;IIIJJJJJ)V
+Landroid/net/NetworkStats$Entry;->add(Landroid/net/NetworkStats$Entry;)V
+Landroid/net/NetworkStats$Entry;->defaultNetwork:I
+Landroid/net/NetworkStats$Entry;->isEmpty()Z
+Landroid/net/NetworkStats$Entry;->isNegative()Z
+Landroid/net/NetworkStats$Entry;->metered:I
+Landroid/net/NetworkStats$Entry;->operations:J
+Landroid/net/NetworkStats$Entry;->roaming:I
+Landroid/net/NetworkStats$NonMonotonicObserver;->foundNonMonotonic(Landroid/net/NetworkStats;ILandroid/net/NetworkStats;ILjava/lang/Object;)V
+Landroid/net/NetworkStats$NonMonotonicObserver;->foundNonMonotonic(Landroid/net/NetworkStats;ILjava/lang/Object;)V
+Landroid/net/NetworkStats;->addIfaceValues(Ljava/lang/String;JJJJ)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->addTrafficToApplications(ILjava/lang/String;Ljava/lang/String;Landroid/net/NetworkStats$Entry;Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats$Entry;
+Landroid/net/NetworkStats;->addValues(Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->addValues(Ljava/lang/String;IIIIIIJJJJJ)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->addValues(Ljava/lang/String;IIIJJJJJ)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->apply464xlatAdjustments(Landroid/net/NetworkStats;Landroid/net/NetworkStats;Ljava/util/Map;)V
+Landroid/net/NetworkStats;->apply464xlatAdjustments(Ljava/util/Map;)V
+Landroid/net/NetworkStats;->CLATD_INTERFACE_PREFIX:Ljava/lang/String;
+Landroid/net/NetworkStats;->clear()V
+Landroid/net/NetworkStats;->combineValues(Ljava/lang/String;IIIJJJJJ)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->combineValues(Ljava/lang/String;IIJJJJJ)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->deductTrafficFromVpnApp(ILjava/lang/String;Landroid/net/NetworkStats$Entry;)V
+Landroid/net/NetworkStats;->defaultNetworkToString(I)Ljava/lang/String;
+Landroid/net/NetworkStats;->DEFAULT_NETWORK_ALL:I
+Landroid/net/NetworkStats;->DEFAULT_NETWORK_NO:I
+Landroid/net/NetworkStats;->DEFAULT_NETWORK_YES:I
+Landroid/net/NetworkStats;->dump(Ljava/lang/String;Ljava/io/PrintWriter;)V
+Landroid/net/NetworkStats;->elapsedRealtime:J
+Landroid/net/NetworkStats;->filter(I[Ljava/lang/String;I)V
+Landroid/net/NetworkStats;->findIndex(Ljava/lang/String;IIIIII)I
+Landroid/net/NetworkStats;->findIndexHinted(Ljava/lang/String;IIIIIII)I
+Landroid/net/NetworkStats;->getElapsedRealtime()J
+Landroid/net/NetworkStats;->getElapsedRealtimeAge()J
+Landroid/net/NetworkStats;->getTotal(Landroid/net/NetworkStats$Entry;Ljava/util/HashSet;)Landroid/net/NetworkStats$Entry;
+Landroid/net/NetworkStats;->getTotal(Landroid/net/NetworkStats$Entry;Ljava/util/HashSet;IZ)Landroid/net/NetworkStats$Entry;
+Landroid/net/NetworkStats;->getTotalPackets()J
+Landroid/net/NetworkStats;->getUniqueIfaces()[Ljava/lang/String;
+Landroid/net/NetworkStats;->groupedByIface()Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->groupedByUid()Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->IFACE_ALL:Ljava/lang/String;
+Landroid/net/NetworkStats;->INTERFACES_ALL:[Ljava/lang/String;
+Landroid/net/NetworkStats;->internalSize()I
+Landroid/net/NetworkStats;->IPV4V6_HEADER_DELTA:I
+Landroid/net/NetworkStats;->meteredToString(I)Ljava/lang/String;
+Landroid/net/NetworkStats;->METERED_ALL:I
+Landroid/net/NetworkStats;->METERED_NO:I
+Landroid/net/NetworkStats;->METERED_YES:I
+Landroid/net/NetworkStats;->migrateTun(ILjava/lang/String;Ljava/lang/String;)Z
+Landroid/net/NetworkStats;->roamingToString(I)Ljava/lang/String;
+Landroid/net/NetworkStats;->ROAMING_ALL:I
+Landroid/net/NetworkStats;->ROAMING_NO:I
+Landroid/net/NetworkStats;->ROAMING_YES:I
+Landroid/net/NetworkStats;->setElapsedRealtime(J)V
+Landroid/net/NetworkStats;->setMatches(II)Z
+Landroid/net/NetworkStats;->setToCheckinString(I)Ljava/lang/String;
+Landroid/net/NetworkStats;->setToString(I)Ljava/lang/String;
+Landroid/net/NetworkStats;->setValues(ILandroid/net/NetworkStats$Entry;)V
+Landroid/net/NetworkStats;->SET_ALL:I
+Landroid/net/NetworkStats;->SET_DBG_VPN_IN:I
+Landroid/net/NetworkStats;->SET_DBG_VPN_OUT:I
+Landroid/net/NetworkStats;->SET_DEBUG_START:I
+Landroid/net/NetworkStats;->SET_DEFAULT:I
+Landroid/net/NetworkStats;->SET_FOREGROUND:I
+Landroid/net/NetworkStats;->spliceOperationsFrom(Landroid/net/NetworkStats;)V
+Landroid/net/NetworkStats;->STATS_PER_IFACE:I
+Landroid/net/NetworkStats;->STATS_PER_UID:I
+Landroid/net/NetworkStats;->subtract(Landroid/net/NetworkStats;)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->subtract(Landroid/net/NetworkStats;Landroid/net/NetworkStats;Landroid/net/NetworkStats$NonMonotonicObserver;Ljava/lang/Object;)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->subtract(Landroid/net/NetworkStats;Landroid/net/NetworkStats;Landroid/net/NetworkStats$NonMonotonicObserver;Ljava/lang/Object;Landroid/net/NetworkStats;)Landroid/net/NetworkStats;
+Landroid/net/NetworkStats;->TAG:Ljava/lang/String;
+Landroid/net/NetworkStats;->tagToString(I)Ljava/lang/String;
+Landroid/net/NetworkStats;->TAG_ALL:I
+Landroid/net/NetworkStats;->TAG_NONE:I
+Landroid/net/NetworkStats;->tunAdjustmentInit(ILjava/lang/String;Ljava/lang/String;Landroid/net/NetworkStats$Entry;Landroid/net/NetworkStats$Entry;)V
+Landroid/net/NetworkStats;->tunGetPool(Landroid/net/NetworkStats$Entry;Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats$Entry;
+Landroid/net/NetworkStats;->tunSubtract(ILandroid/net/NetworkStats;Landroid/net/NetworkStats$Entry;)V
+Landroid/net/NetworkStats;->UID_ALL:I
+Landroid/net/NetworkStats;->withoutUids([I)Landroid/net/NetworkStats;
+Landroid/net/NetworkStatsHistory$DataStreamUtils;-><init>()V
+Landroid/net/NetworkStatsHistory$DataStreamUtils;->readFullLongArray(Ljava/io/DataInputStream;)[J
+Landroid/net/NetworkStatsHistory$DataStreamUtils;->readVarLong(Ljava/io/DataInputStream;)J
+Landroid/net/NetworkStatsHistory$DataStreamUtils;->readVarLongArray(Ljava/io/DataInputStream;)[J
+Landroid/net/NetworkStatsHistory$DataStreamUtils;->writeVarLong(Ljava/io/DataOutputStream;J)V
+Landroid/net/NetworkStatsHistory$DataStreamUtils;->writeVarLongArray(Ljava/io/DataOutputStream;[JI)V
+Landroid/net/NetworkStatsHistory$Entry;-><init>()V
+Landroid/net/NetworkStatsHistory$Entry;->activeTime:J
+Landroid/net/NetworkStatsHistory$Entry;->operations:J
+Landroid/net/NetworkStatsHistory$Entry;->UNKNOWN:J
+Landroid/net/NetworkStatsHistory$ParcelUtils;-><init>()V
+Landroid/net/NetworkStatsHistory$ParcelUtils;->readLongArray(Landroid/os/Parcel;)[J
+Landroid/net/NetworkStatsHistory$ParcelUtils;->writeLongArray(Landroid/os/Parcel;[JI)V
+Landroid/net/NetworkStatsHistory;-><init>(JI)V
+Landroid/net/NetworkStatsHistory;-><init>(JII)V
+Landroid/net/NetworkStatsHistory;-><init>(Landroid/net/NetworkStatsHistory;J)V
+Landroid/net/NetworkStatsHistory;-><init>(Ljava/io/DataInputStream;)V
+Landroid/net/NetworkStatsHistory;->activeTime:[J
+Landroid/net/NetworkStatsHistory;->addLong([JIJ)V
+Landroid/net/NetworkStatsHistory;->bucketCount:I
+Landroid/net/NetworkStatsHistory;->bucketDuration:J
+Landroid/net/NetworkStatsHistory;->bucketStart:[J
+Landroid/net/NetworkStatsHistory;->clear()V
+Landroid/net/NetworkStatsHistory;->dump(Lcom/android/internal/util/IndentingPrintWriter;Z)V
+Landroid/net/NetworkStatsHistory;->dumpCheckin(Ljava/io/PrintWriter;)V
+Landroid/net/NetworkStatsHistory;->ensureBuckets(JJ)V
+Landroid/net/NetworkStatsHistory;->estimateResizeBuckets(J)I
+Landroid/net/NetworkStatsHistory;->FIELD_ACTIVE_TIME:I
+Landroid/net/NetworkStatsHistory;->FIELD_ALL:I
+Landroid/net/NetworkStatsHistory;->FIELD_OPERATIONS:I
+Landroid/net/NetworkStatsHistory;->FIELD_RX_BYTES:I
+Landroid/net/NetworkStatsHistory;->FIELD_RX_PACKETS:I
+Landroid/net/NetworkStatsHistory;->FIELD_TX_BYTES:I
+Landroid/net/NetworkStatsHistory;->FIELD_TX_PACKETS:I
+Landroid/net/NetworkStatsHistory;->generateRandom(JJJ)V
+Landroid/net/NetworkStatsHistory;->generateRandom(JJJJJJJLjava/util/Random;)V
+Landroid/net/NetworkStatsHistory;->getBucketDuration()J
+Landroid/net/NetworkStatsHistory;->getIndexAfter(J)I
+Landroid/net/NetworkStatsHistory;->getLong([JIJ)J
+Landroid/net/NetworkStatsHistory;->getTotalBytes()J
+Landroid/net/NetworkStatsHistory;->insertBucket(IJ)V
+Landroid/net/NetworkStatsHistory;->intersects(JJ)Z
+Landroid/net/NetworkStatsHistory;->operations:[J
+Landroid/net/NetworkStatsHistory;->randomLong(Ljava/util/Random;JJ)J
+Landroid/net/NetworkStatsHistory;->recordData(JJJJ)V
+Landroid/net/NetworkStatsHistory;->recordData(JJLandroid/net/NetworkStats$Entry;)V
+Landroid/net/NetworkStatsHistory;->recordHistory(Landroid/net/NetworkStatsHistory;JJ)V
+Landroid/net/NetworkStatsHistory;->removeBucketsBefore(J)V
+Landroid/net/NetworkStatsHistory;->rxBytes:[J
+Landroid/net/NetworkStatsHistory;->rxPackets:[J
+Landroid/net/NetworkStatsHistory;->setLong([JIJ)V
+Landroid/net/NetworkStatsHistory;->setValues(ILandroid/net/NetworkStatsHistory$Entry;)V
+Landroid/net/NetworkStatsHistory;->totalBytes:J
+Landroid/net/NetworkStatsHistory;->txBytes:[J
+Landroid/net/NetworkStatsHistory;->txPackets:[J
+Landroid/net/NetworkStatsHistory;->VERSION_ADD_ACTIVE:I
+Landroid/net/NetworkStatsHistory;->VERSION_ADD_PACKETS:I
+Landroid/net/NetworkStatsHistory;->VERSION_INIT:I
+Landroid/net/NetworkStatsHistory;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+Landroid/net/NetworkStatsHistory;->writeToProto(Landroid/util/proto/ProtoOutputStream;J[JI)V
+Landroid/net/NetworkStatsHistory;->writeToStream(Ljava/io/DataOutputStream;)V
+Landroid/net/NetworkTemplate;-><init>(ILjava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V
+Landroid/net/NetworkTemplate;-><init>(ILjava/lang/String;[Ljava/lang/String;Ljava/lang/String;III)V
+Landroid/net/NetworkTemplate;-><init>(Landroid/os/Parcel;)V
+Landroid/net/NetworkTemplate;->BACKUP_VERSION:I
+Landroid/net/NetworkTemplate;->buildTemplateBluetooth()Landroid/net/NetworkTemplate;
+Landroid/net/NetworkTemplate;->buildTemplateProxy()Landroid/net/NetworkTemplate;
+Landroid/net/NetworkTemplate;->buildTemplateWifi(Ljava/lang/String;)Landroid/net/NetworkTemplate;
+Landroid/net/NetworkTemplate;->forceAllNetworkTypes()V
+Landroid/net/NetworkTemplate;->getBytesForBackup()[B
+Landroid/net/NetworkTemplate;->getMatchRuleName(I)Ljava/lang/String;
+Landroid/net/NetworkTemplate;->getNetworkId()Ljava/lang/String;
+Landroid/net/NetworkTemplate;->getNetworkTemplateFromBackup(Ljava/io/DataInputStream;)Landroid/net/NetworkTemplate;
+Landroid/net/NetworkTemplate;->isKnownMatchRule(I)Z
+Landroid/net/NetworkTemplate;->isMatchRuleMobile()Z
+Landroid/net/NetworkTemplate;->isPersistable()Z
+Landroid/net/NetworkTemplate;->matches(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesBluetooth(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesDefaultNetwork(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesEthernet(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesMetered(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesMobile(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesMobileWildcard(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesProxy(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesRoaming(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesSubscriberId(Ljava/lang/String;)Z
+Landroid/net/NetworkTemplate;->matchesWifi(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->matchesWifiWildcard(Landroid/net/NetworkIdentity;)Z
+Landroid/net/NetworkTemplate;->MATCH_BLUETOOTH:I
+Landroid/net/NetworkTemplate;->MATCH_ETHERNET:I
+Landroid/net/NetworkTemplate;->MATCH_MOBILE:I
+Landroid/net/NetworkTemplate;->MATCH_MOBILE_WILDCARD:I
+Landroid/net/NetworkTemplate;->MATCH_PROXY:I
+Landroid/net/NetworkTemplate;->MATCH_WIFI:I
+Landroid/net/NetworkTemplate;->MATCH_WIFI_WILDCARD:I
+Landroid/net/NetworkTemplate;->mDefaultNetwork:I
+Landroid/net/NetworkTemplate;->mMatchRule:I
+Landroid/net/NetworkTemplate;->mMatchSubscriberIds:[Ljava/lang/String;
+Landroid/net/NetworkTemplate;->mMetered:I
+Landroid/net/NetworkTemplate;->mNetworkId:Ljava/lang/String;
+Landroid/net/NetworkTemplate;->mRoaming:I
+Landroid/net/NetworkTemplate;->mSubscriberId:Ljava/lang/String;
+Landroid/net/NetworkTemplate;->sForceAllNetworkTypes:Z
+Landroid/net/NetworkTemplate;->TAG:Ljava/lang/String;
+Landroid/net/NetworkUtils;-><init>()V
+Landroid/net/NetworkUtils;->addressTypeMatches(Ljava/net/InetAddress;Ljava/net/InetAddress;)Z
+Landroid/net/NetworkUtils;->bindProcessToNetwork(I)Z
+Landroid/net/NetworkUtils;->bindProcessToNetworkForHostResolution(I)Z
+Landroid/net/NetworkUtils;->bindSocketToNetwork(II)I
+Landroid/net/NetworkUtils;->deduplicatePrefixSet(Ljava/util/TreeSet;)Ljava/util/TreeSet;
+Landroid/net/NetworkUtils;->getBoundNetworkForProcess()I
+Landroid/net/NetworkUtils;->getNetworkPart(Ljava/net/InetAddress;I)Ljava/net/InetAddress;
+Landroid/net/NetworkUtils;->hexToInet6Address(Ljava/lang/String;)Ljava/net/InetAddress;
+Landroid/net/NetworkUtils;->inetAddressToInt(Ljava/net/Inet4Address;)I
+Landroid/net/NetworkUtils;->makeStrings(Ljava/util/Collection;)[Ljava/lang/String;
+Landroid/net/NetworkUtils;->maskRawAddress([BI)V
+Landroid/net/NetworkUtils;->netmaskIntToPrefixLength(I)I
+Landroid/net/NetworkUtils;->parcelInetAddress(Landroid/os/Parcel;Ljava/net/InetAddress;I)V
+Landroid/net/NetworkUtils;->parseIpAndMask(Ljava/lang/String;)Landroid/util/Pair;
+Landroid/net/NetworkUtils;->protectFromVpn(I)Z
+Landroid/net/NetworkUtils;->queryUserAccess(II)Z
+Landroid/net/NetworkUtils;->routedIPv4AddressCount(Ljava/util/TreeSet;)J
+Landroid/net/NetworkUtils;->routedIPv6AddressCount(Ljava/util/TreeSet;)Ljava/math/BigInteger;
+Landroid/net/NetworkUtils;->setupRaSocket(Ljava/io/FileDescriptor;I)V
+Landroid/net/NetworkUtils;->TAG:Ljava/lang/String;
+Landroid/net/NetworkUtils;->unparcelInetAddress(Landroid/os/Parcel;)Ljava/net/InetAddress;
+Landroid/net/NetworkWatchlistManager;-><init>(Landroid/content/Context;)V
+Landroid/net/NetworkWatchlistManager;-><init>(Landroid/content/Context;Lcom/android/internal/net/INetworkWatchlistManager;)V
+Landroid/net/NetworkWatchlistManager;->getWatchlistConfigHash()[B
+Landroid/net/NetworkWatchlistManager;->mContext:Landroid/content/Context;
+Landroid/net/NetworkWatchlistManager;->mNetworkWatchlistManager:Lcom/android/internal/net/INetworkWatchlistManager;
+Landroid/net/NetworkWatchlistManager;->reloadWatchlist()V
+Landroid/net/NetworkWatchlistManager;->reportWatchlistIfNecessary()V
+Landroid/net/NetworkWatchlistManager;->SHARED_MEMORY_TAG:Ljava/lang/String;
+Landroid/net/NetworkWatchlistManager;->TAG:Ljava/lang/String;
+Landroid/net/ProxyInfo;-><init>(Landroid/net/ProxyInfo;)V
+Landroid/net/ProxyInfo;-><init>(Landroid/net/Uri;)V
+Landroid/net/ProxyInfo;-><init>(Landroid/net/Uri;I)V
+Landroid/net/ProxyInfo;-><init>(Ljava/lang/String;)V
+Landroid/net/ProxyInfo;-><init>(Ljava/lang/String;ILjava/lang/String;[Ljava/lang/String;)V
+Landroid/net/ProxyInfo;->getExclusionListAsString()Ljava/lang/String;
+Landroid/net/ProxyInfo;->getSocketAddress()Ljava/net/InetSocketAddress;
+Landroid/net/ProxyInfo;->isValid()Z
+Landroid/net/ProxyInfo;->LOCAL_EXCL_LIST:Ljava/lang/String;
+Landroid/net/ProxyInfo;->LOCAL_HOST:Ljava/lang/String;
+Landroid/net/ProxyInfo;->LOCAL_PORT:I
+Landroid/net/ProxyInfo;->makeProxy()Ljava/net/Proxy;
+Landroid/net/ProxyInfo;->mExclusionList:Ljava/lang/String;
+Landroid/net/ProxyInfo;->mHost:Ljava/lang/String;
+Landroid/net/ProxyInfo;->mPacFileUrl:Landroid/net/Uri;
+Landroid/net/ProxyInfo;->mParsedExclusionList:[Ljava/lang/String;
+Landroid/net/ProxyInfo;->mPort:I
+Landroid/net/ProxyInfo;->setExclusionList(Ljava/lang/String;)V
+Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;)V
+Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;I)V
+Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;Ljava/net/InetAddress;)V
+Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;Ljava/net/InetAddress;Ljava/lang/String;I)V
+Landroid/net/RouteInfo;-><init>(Landroid/net/LinkAddress;)V
+Landroid/net/RouteInfo;->getDestinationLinkAddress()Landroid/net/LinkAddress;
+Landroid/net/RouteInfo;->getType()I
+Landroid/net/RouteInfo;->isHostRoute()Z
+Landroid/net/RouteInfo;->isIPv4Default()Z
+Landroid/net/RouteInfo;->isIPv6Default()Z
+Landroid/net/RouteInfo;->makeHostRoute(Ljava/net/InetAddress;Ljava/lang/String;)Landroid/net/RouteInfo;
+Landroid/net/RouteInfo;->makeHostRoute(Ljava/net/InetAddress;Ljava/net/InetAddress;Ljava/lang/String;)Landroid/net/RouteInfo;
+Landroid/net/RouteInfo;->mDestination:Landroid/net/IpPrefix;
+Landroid/net/RouteInfo;->mHasGateway:Z
+Landroid/net/RouteInfo;->mInterface:Ljava/lang/String;
+Landroid/net/RouteInfo;->mType:I
+Landroid/net/RouteInfo;->RTN_THROW:I
+Landroid/net/RouteInfo;->RTN_UNICAST:I
+Landroid/net/RouteInfo;->RTN_UNREACHABLE:I
+Landroid/net/StaticIpConfiguration;-><init>(Landroid/net/StaticIpConfiguration;)V
+Landroid/net/StaticIpConfiguration;->clear()V
+Landroid/net/StaticIpConfiguration;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/net/StaticIpConfiguration;->readFromParcel(Landroid/net/StaticIpConfiguration;Landroid/os/Parcel;)V
+Landroid/net/StaticIpConfiguration;->toLinkProperties(Ljava/lang/String;)Landroid/net/LinkProperties;
+Landroid/net/UidRange;-><init>(II)V
+Landroid/net/UidRange;->contains(I)Z
+Landroid/net/UidRange;->containsRange(Landroid/net/UidRange;)Z
+Landroid/net/UidRange;->count()I
+Landroid/net/UidRange;->createForUser(I)Landroid/net/UidRange;
+Landroid/net/UidRange;->CREATOR:Landroid/os/Parcelable$Creator;
+Landroid/net/UidRange;->getStartUser()I
+Landroid/net/UidRange;->start:I
+Landroid/net/UidRange;->stop:I
diff --git a/Tethering/apex/hiddenapi/hiddenapi-max-target-r-loprio.txt b/Tethering/apex/hiddenapi/hiddenapi-max-target-r-loprio.txt
new file mode 100644
index 0000000..211b847
--- /dev/null
+++ b/Tethering/apex/hiddenapi/hiddenapi-max-target-r-loprio.txt
@@ -0,0 +1 @@
+Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager;
diff --git a/Tethering/apex/hiddenapi/hiddenapi-unsupported.txt b/Tethering/apex/hiddenapi/hiddenapi-unsupported.txt
new file mode 100644
index 0000000..f89906f
--- /dev/null
+++ b/Tethering/apex/hiddenapi/hiddenapi-unsupported.txt
@@ -0,0 +1,10 @@
+Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworkInfo()[Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworks()[Landroid/net/Network;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableIfaces()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableUsbRegexs()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredIfaces()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IConnectivityManager;
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 611c828..7189933 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -29,9 +29,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.net.module.util.BpfMap;
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.BpfMap;
import com.android.networkstack.tethering.BpfUtils;
import com.android.networkstack.tethering.Tether4Key;
import com.android.networkstack.tethering.Tether4Value;
diff --git a/Tethering/bpf_progs/Android.bp b/Tethering/bpf_progs/Android.bp
deleted file mode 100644
index 5b00dfe..0000000
--- a/Tethering/bpf_progs/Android.bp
+++ /dev/null
@@ -1,60 +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.
-//
-
-//
-// struct definitions shared with JNI
-//
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_headers {
- name: "bpf_tethering_headers",
- vendor_available: false,
- host_supported: false,
- export_include_dirs: ["."],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- sdk_version: "30",
- min_sdk_version: "30",
- apex_available: ["com.android.tethering"],
- visibility: [
- "//packages/modules/Connectivity/Tethering",
- ],
-}
-
-//
-// bpf kernel programs
-//
-bpf {
- name: "offload.o",
- srcs: ["offload.c"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
-
-bpf {
- name: "test.o",
- srcs: ["test.c"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index f652772..c82a993 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -22,7 +22,19 @@
defaults: ["framework-module-defaults"],
impl_library_visibility: [
"//packages/modules/Connectivity/Tethering:__subpackages__",
+
+ // Using for test only
+ "//cts/tests/netlegacy22.api",
+ "//external/sl4a:__subpackages__",
+ "//frameworks/base/packages/Connectivity/tests:__subpackages__",
+ "//frameworks/libs/net/common/testutils",
+ "//frameworks/libs/net/common/tests:__subpackages__",
+ "//frameworks/opt/telephony/tests/telephonytests",
+ "//packages/modules/CaptivePortalLogin/tests",
+ "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
"//packages/modules/Connectivity/tests:__subpackages__",
+ "//packages/modules/NetworkStack/tests:__subpackages__",
+ "//packages/modules/Wifi/service/tests/wifitests",
],
srcs: [":framework-tethering-srcs"],
@@ -41,6 +53,7 @@
apex_available: ["com.android.tethering"],
permitted_packages: ["android.net"],
min_sdk_version: "30",
+ lint: { strict_updatability_linting: true },
}
filegroup {
diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt
index 0566040..460c216 100644
--- a/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -27,6 +27,15 @@
method @Deprecated public int untether(@NonNull String);
}
+ public static interface TetheringManager.TetheredInterfaceCallback {
+ method public void onAvailable(@NonNull String);
+ method public void onUnavailable();
+ }
+
+ public static interface TetheringManager.TetheredInterfaceRequest {
+ method public void release();
+ }
+
public static interface TetheringManager.TetheringEventCallback {
method @Deprecated public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
}
diff --git a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index cf094aa..77e78bd 100644
--- a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -49,4 +49,6 @@
void stopAllTethering(String callerPkg, String callingAttributionTag,
IIntResultListener receiver);
+
+ void setPreferTestNetworks(boolean prefer, IIntResultListener listener);
}
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 73ab908..6f9b33e 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Bundle;
@@ -37,6 +38,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -265,7 +267,7 @@
public TetheringManager(@NonNull final Context context,
@NonNull Supplier<IBinder> connectorSupplier) {
mContext = context;
- mCallback = new TetheringCallbackInternal();
+ mCallback = new TetheringCallbackInternal(this);
mConnectorSupplier = connectorSupplier;
final String pkgName = mContext.getOpPackageName();
@@ -289,6 +291,23 @@
getConnector(c -> c.registerTetheringEventCallback(mCallback, pkgName));
}
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ final String pkgName = mContext.getOpPackageName();
+ Log.i(TAG, "unregisterTetheringEventCallback:" + pkgName);
+ // 1. It's generally not recommended to perform long operations in finalize, but while
+ // unregisterTetheringEventCallback does an IPC, it's a oneway IPC so should not block.
+ // 2. If the connector is not yet connected, TetheringManager is impossible to finalize
+ // because the connector polling thread strong reference the TetheringManager object. So
+ // it's guaranteed that registerTetheringEventCallback was already called before calling
+ // unregisterTetheringEventCallback in finalize.
+ if (mConnector == null) Log.wtf(TAG, "null connector in finalize!");
+ getConnector(c -> c.unregisterTetheringEventCallback(mCallback, pkgName));
+
+ super.finalize();
+ }
+
private void startPollingForConnector() {
new Thread(() -> {
while (true) {
@@ -415,7 +434,7 @@
}
}
- private void throwIfPermissionFailure(final int errorCode) {
+ private static void throwIfPermissionFailure(final int errorCode) {
switch (errorCode) {
case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:
throw new SecurityException("No android.permission.TETHER_PRIVILEGED"
@@ -426,21 +445,78 @@
}
}
- private class TetheringCallbackInternal extends ITetheringEventCallback.Stub {
+ /**
+ * A request for a tethered interface.
+ *
+ * There are two reasons why this doesn't implement CLoseable:
+ * 1. To consistency with the existing EthernetManager.TetheredInterfaceRequest, which is
+ * already released.
+ * 2. This is not synchronous, so it's not useful to use try-with-resources.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @SuppressLint("NotCloseable")
+ public interface TetheredInterfaceRequest {
+ /**
+ * Release the request to tear down tethered interface.
+ */
+ void release();
+ }
+
+ /**
+ * Callback for requestTetheredInterface.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public interface TetheredInterfaceCallback {
+ /**
+ * Called when the tethered interface is available.
+ * @param iface The name of the interface.
+ */
+ void onAvailable(@NonNull String iface);
+
+ /**
+ * Called when the tethered interface is now unavailable.
+ */
+ void onUnavailable();
+ }
+
+ private static class TetheringCallbackInternal extends ITetheringEventCallback.Stub {
private volatile int mError = TETHER_ERROR_NO_ERROR;
private final ConditionVariable mWaitForCallback = new ConditionVariable();
+ // This object is never garbage collected because the Tethering code running in
+ // the system server always maintains a reference to it for as long as
+ // mCallback is registered.
+ //
+ // Don't keep a strong reference to TetheringManager because otherwise
+ // TetheringManager cannot be garbage collected, and because TetheringManager
+ // stores the Context that it was created from, this will prevent the calling
+ // Activity from being garbage collected as well.
+ private final WeakReference<TetheringManager> mTetheringMgrRef;
+
+ TetheringCallbackInternal(final TetheringManager tm) {
+ mTetheringMgrRef = new WeakReference<>(tm);
+ }
@Override
public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
- mTetheringConfiguration = parcel.config;
- mTetherStatesParcel = parcel.states;
- mWaitForCallback.open();
+ TetheringManager tetheringMgr = mTetheringMgrRef.get();
+ if (tetheringMgr != null) {
+ tetheringMgr.mTetheringConfiguration = parcel.config;
+ tetheringMgr.mTetherStatesParcel = parcel.states;
+ mWaitForCallback.open();
+ }
}
@Override
public void onCallbackStopped(int errorCode) {
- mError = errorCode;
- mWaitForCallback.open();
+ TetheringManager tetheringMgr = mTetheringMgrRef.get();
+ if (tetheringMgr != null) {
+ mError = errorCode;
+ mWaitForCallback.open();
+ }
}
@Override
@@ -448,12 +524,14 @@
@Override
public void onConfigurationChanged(TetheringConfigurationParcel config) {
- mTetheringConfiguration = config;
+ TetheringManager tetheringMgr = mTetheringMgrRef.get();
+ if (tetheringMgr != null) tetheringMgr.mTetheringConfiguration = config;
}
@Override
public void onTetherStatesChanged(TetherStatesParcel states) {
- mTetherStatesParcel = states;
+ TetheringManager tetheringMgr = mTetheringMgrRef.get();
+ if (tetheringMgr != null) tetheringMgr.mTetherStatesParcel = states;
}
@Override
@@ -505,7 +583,7 @@
* @return the context's attribution tag
*/
private @Nullable String getAttributionTag() {
- return null;
+ return mContext.getAttributionTag();
}
/**
@@ -1538,4 +1616,25 @@
}
}));
}
+
+ /**
+ * Whether to treat networks that have TRANSPORT_TEST as Tethering upstreams. The effects of
+ * this method apply to any test networks that are already present on the system.
+ *
+ * @throws SecurityException If the caller doesn't have the NETWORK_SETTINGS permission.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setPreferTestNetworks(final boolean prefer) {
+ Log.i(TAG, "setPreferTestNetworks caller: " + mContext.getOpPackageName());
+
+ final RequestDispatcher dispatcher = new RequestDispatcher();
+ final int ret = dispatcher.waitForResult((connector, listener) -> {
+ try {
+ connector.setPreferTestNetworks(prefer, listener);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ });
+ }
}
diff --git a/Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp b/Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp
deleted file mode 100644
index 260dbc1..0000000
--- a/Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp
+++ /dev/null
@@ -1,136 +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 <errno.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-#include "nativehelper/scoped_primitive_array.h"
-#include "nativehelper/scoped_utf_chars.h"
-
-#define BPF_FD_JUST_USE_INT
-#include "BpfSyscallWrappers.h"
-
-namespace android {
-
-static jint com_android_networkstack_tethering_BpfMap_closeMap(JNIEnv *env, jobject clazz,
- jint fd) {
- int ret = close(fd);
-
- if (ret) jniThrowErrnoException(env, "closeMap", errno);
-
- return ret;
-}
-
-static jint com_android_networkstack_tethering_BpfMap_bpfFdGet(JNIEnv *env, jobject clazz,
- jstring path, jint mode) {
- ScopedUtfChars pathname(env, path);
-
- jint fd = bpf::bpfFdGet(pathname.c_str(), static_cast<unsigned>(mode));
-
- if (fd < 0) jniThrowErrnoException(env, "bpfFdGet", errno);
-
- return fd;
-}
-
-static void com_android_networkstack_tethering_BpfMap_writeToMapEntry(JNIEnv *env, jobject clazz,
- jint fd, jbyteArray key, jbyteArray value, jint flags) {
- ScopedByteArrayRO keyRO(env, key);
- ScopedByteArrayRO valueRO(env, value);
-
- int ret = bpf::writeToMapEntry(static_cast<int>(fd), keyRO.get(), valueRO.get(),
- static_cast<int>(flags));
-
- if (ret) jniThrowErrnoException(env, "writeToMapEntry", errno);
-}
-
-static jboolean throwIfNotEnoent(JNIEnv *env, const char* functionName, int ret, int err) {
- if (ret == 0) return true;
-
- if (err != ENOENT) jniThrowErrnoException(env, functionName, err);
- return false;
-}
-
-static jboolean com_android_networkstack_tethering_BpfMap_deleteMapEntry(JNIEnv *env, jobject clazz,
- jint fd, jbyteArray key) {
- ScopedByteArrayRO keyRO(env, key);
-
- // On success, zero is returned. If the element is not found, -1 is returned and errno is set
- // to ENOENT.
- int ret = bpf::deleteMapEntry(static_cast<int>(fd), keyRO.get());
-
- return throwIfNotEnoent(env, "deleteMapEntry", ret, errno);
-}
-
-static jboolean com_android_networkstack_tethering_BpfMap_getNextMapKey(JNIEnv *env, jobject clazz,
- jint fd, jbyteArray key, jbyteArray nextKey) {
- // If key is found, the operation returns zero and sets the next key pointer to the key of the
- // next element. If key is not found, the operation returns zero and sets the next key pointer
- // to the key of the first element. If key is the last element, -1 is returned and errno is
- // set to ENOENT. Other possible errno values are ENOMEM, EFAULT, EPERM, and EINVAL.
- ScopedByteArrayRW nextKeyRW(env, nextKey);
- int ret;
- if (key == nullptr) {
- // Called by getFirstKey. Find the first key in the map.
- ret = bpf::getNextMapKey(static_cast<int>(fd), nullptr, nextKeyRW.get());
- } else {
- ScopedByteArrayRO keyRO(env, key);
- ret = bpf::getNextMapKey(static_cast<int>(fd), keyRO.get(), nextKeyRW.get());
- }
-
- return throwIfNotEnoent(env, "getNextMapKey", ret, errno);
-}
-
-static jboolean com_android_networkstack_tethering_BpfMap_findMapEntry(JNIEnv *env, jobject clazz,
- jint fd, jbyteArray key, jbyteArray value) {
- ScopedByteArrayRO keyRO(env, key);
- ScopedByteArrayRW valueRW(env, value);
-
- // If an element is found, the operation returns zero and stores the element's value into
- // "value". If no element is found, the operation returns -1 and sets errno to ENOENT.
- int ret = bpf::findMapEntry(static_cast<int>(fd), keyRO.get(), valueRW.get());
-
- return throwIfNotEnoent(env, "findMapEntry", ret, errno);
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "closeMap", "(I)I",
- (void*) com_android_networkstack_tethering_BpfMap_closeMap },
- { "bpfFdGet", "(Ljava/lang/String;I)I",
- (void*) com_android_networkstack_tethering_BpfMap_bpfFdGet },
- { "writeToMapEntry", "(I[B[BI)V",
- (void*) com_android_networkstack_tethering_BpfMap_writeToMapEntry },
- { "deleteMapEntry", "(I[B)Z",
- (void*) com_android_networkstack_tethering_BpfMap_deleteMapEntry },
- { "getNextMapKey", "(I[B[B)Z",
- (void*) com_android_networkstack_tethering_BpfMap_getNextMapKey },
- { "findMapEntry", "(I[B[B)Z",
- (void*) com_android_networkstack_tethering_BpfMap_findMapEntry },
-
-};
-
-int register_com_android_networkstack_tethering_BpfMap(JNIEnv* env) {
- return jniRegisterNativeMethods(env,
- "com/android/networkstack/tethering/BpfMap",
- gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
deleted file mode 100644
index 2d679a8..0000000
--- a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <arpa/inet.h>
-#include <jni.h>
-#include <linux/if_arp.h>
-#include <linux/if_ether.h>
-#include <linux/netlink.h>
-#include <linux/pkt_cls.h>
-#include <linux/pkt_sched.h>
-#include <linux/rtnetlink.h>
-#include <nativehelper/JNIHelp.h>
-#include <net/if.h>
-#include <stdio.h>
-#include <sys/socket.h>
-
-// TODO: use unique_fd.
-#define BPF_FD_JUST_USE_INT
-#include "BpfSyscallWrappers.h"
-#include "bpf_tethering.h"
-#include "nativehelper/scoped_utf_chars.h"
-
-// The maximum length of TCA_BPF_NAME. Sync from net/sched/cls_bpf.c.
-#define CLS_BPF_NAME_LEN 256
-
-// Classifier name. See cls_bpf_ops in net/sched/cls_bpf.c.
-#define CLS_BPF_KIND_NAME "bpf"
-
-namespace android {
-// Sync from system/netd/server/NetlinkCommands.h
-const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
-const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
-
-static void throwIOException(JNIEnv *env, const char* msg, int error) {
- jniThrowExceptionFmt(env, "java/io/IOException", "%s: %s", msg, strerror(error));
-}
-
-// TODO: move to frameworks/libs/net/common/native for sharing with
-// system/netd/server/OffloadUtils.{c, h}.
-static void sendAndProcessNetlinkResponse(JNIEnv* env, const void* req, int len) {
- int fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); // TODO: use unique_fd
- if (fd == -1) {
- throwIOException(env, "socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE)", errno);
- return;
- }
-
- static constexpr int on = 1;
- if (setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on))) {
- throwIOException(env, "setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, 1)", errno);
- close(fd);
- return;
- }
-
- // this is needed to get valid strace netlink parsing, it allocates the pid
- if (bind(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR))) {
- throwIOException(env, "bind(fd, {AF_NETLINK, 0, 0})", errno);
- close(fd);
- return;
- }
-
- // we do not want to receive messages from anyone besides the kernel
- if (connect(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR))) {
- throwIOException(env, "connect(fd, {AF_NETLINK, 0, 0})", errno);
- close(fd);
- return;
- }
-
- int rv = send(fd, req, len, 0);
-
- if (rv == -1) {
- throwIOException(env, "send(fd, req, len, 0)", errno);
- close(fd);
- return;
- }
-
- if (rv != len) {
- throwIOException(env, "send(fd, req, len, 0)", EMSGSIZE);
- close(fd);
- return;
- }
-
- struct {
- nlmsghdr h;
- nlmsgerr e;
- char buf[256];
- } resp = {};
-
- rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
-
- if (rv == -1) {
- throwIOException(env, "recv() failed", errno);
- close(fd);
- return;
- }
-
- if (rv < (int)NLMSG_SPACE(sizeof(struct nlmsgerr))) {
- jniThrowExceptionFmt(env, "java/io/IOException", "recv() returned short packet: %d", rv);
- close(fd);
- return;
- }
-
- if (resp.h.nlmsg_len != (unsigned)rv) {
- jniThrowExceptionFmt(env, "java/io/IOException",
- "recv() returned invalid header length: %d != %d", resp.h.nlmsg_len,
- rv);
- close(fd);
- return;
- }
-
- if (resp.h.nlmsg_type != NLMSG_ERROR) {
- jniThrowExceptionFmt(env, "java/io/IOException",
- "recv() did not return NLMSG_ERROR message: %d", resp.h.nlmsg_type);
- close(fd);
- return;
- }
-
- if (resp.e.error) { // returns 0 on success
- throwIOException(env, "NLMSG_ERROR message return error", -resp.e.error);
- }
- close(fd);
- return;
-}
-
-static int hardwareAddressType(const char* interface) {
- int fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (fd < 0) return -errno;
-
- struct ifreq ifr = {};
- // We use strncpy() instead of strlcpy() since kernel has to be able
- // to handle non-zero terminated junk passed in by userspace anyway,
- // and this way too long interface names (more than IFNAMSIZ-1 = 15
- // characters plus terminating NULL) will not get truncated to 15
- // characters and zero-terminated and thus potentially erroneously
- // match a truncated interface if one were to exist.
- strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
-
- int rv;
- if (ioctl(fd, SIOCGIFHWADDR, &ifr, sizeof(ifr))) {
- rv = -errno;
- } else {
- rv = ifr.ifr_hwaddr.sa_family;
- }
-
- close(fd);
- return rv;
-}
-
-static jboolean com_android_networkstack_tethering_BpfUtils_isEthernet(JNIEnv* env, jobject clazz,
- jstring iface) {
- ScopedUtfChars interface(env, iface);
-
- int rv = hardwareAddressType(interface.c_str());
- if (rv < 0) {
- jniThrowExceptionFmt(env, "java/io/IOException",
- "Get hardware address type of interface %s failed: %s",
- interface.c_str(), strerror(-rv));
- return false;
- }
-
- switch (rv) {
- case ARPHRD_ETHER:
- return true;
- case ARPHRD_NONE:
- case ARPHRD_RAWIP: // in Linux 4.14+ rmnet support was upstreamed and this is 519
- case 530: // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
- return false;
- default:
- jniThrowExceptionFmt(env, "java/io/IOException",
- "Unknown hardware address type %d on interface %s", rv,
- interface.c_str());
- return false;
- }
-}
-
-// tc filter add dev .. in/egress prio 1 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
-// direct-action
-static void com_android_networkstack_tethering_BpfUtils_tcFilterAddDevBpf(
- JNIEnv* env, jobject clazz, jint ifIndex, jboolean ingress, jshort prio, jshort proto,
- jstring bpfProgPath) {
- ScopedUtfChars pathname(env, bpfProgPath);
-
- const int bpfFd = bpf::retrieveProgram(pathname.c_str());
- if (bpfFd == -1) {
- throwIOException(env, "retrieveProgram failed", errno);
- return;
- }
-
- struct {
- nlmsghdr n;
- tcmsg t;
- struct {
- nlattr attr;
- // The maximum classifier name length is defined in
- // tcf_proto_ops in include/net/sch_generic.h.
- char str[NLMSG_ALIGN(sizeof(CLS_BPF_KIND_NAME))];
- } kind;
- struct {
- nlattr attr;
- struct {
- nlattr attr;
- __u32 u32;
- } fd;
- struct {
- nlattr attr;
- char str[NLMSG_ALIGN(CLS_BPF_NAME_LEN)];
- } name;
- struct {
- nlattr attr;
- __u32 u32;
- } flags;
- } options;
- } req = {
- .n =
- {
- .nlmsg_len = sizeof(req),
- .nlmsg_type = RTM_NEWTFILTER,
- .nlmsg_flags = NETLINK_REQUEST_FLAGS | NLM_F_EXCL | NLM_F_CREATE,
- },
- .t =
- {
- .tcm_family = AF_UNSPEC,
- .tcm_ifindex = ifIndex,
- .tcm_handle = TC_H_UNSPEC,
- .tcm_parent = TC_H_MAKE(TC_H_CLSACT,
- ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
- .tcm_info = static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
- htons(static_cast<uint16_t>(proto))),
- },
- .kind =
- {
- .attr =
- {
- .nla_len = sizeof(req.kind),
- .nla_type = TCA_KIND,
- },
- .str = CLS_BPF_KIND_NAME,
- },
- .options =
- {
- .attr =
- {
- .nla_len = sizeof(req.options),
- .nla_type = NLA_F_NESTED | TCA_OPTIONS,
- },
- .fd =
- {
- .attr =
- {
- .nla_len = sizeof(req.options.fd),
- .nla_type = TCA_BPF_FD,
- },
- .u32 = static_cast<__u32>(bpfFd),
- },
- .name =
- {
- .attr =
- {
- .nla_len = sizeof(req.options.name),
- .nla_type = TCA_BPF_NAME,
- },
- // Visible via 'tc filter show', but
- // is overwritten by strncpy below
- .str = "placeholder",
- },
- .flags =
- {
- .attr =
- {
- .nla_len = sizeof(req.options.flags),
- .nla_type = TCA_BPF_FLAGS,
- },
- .u32 = TCA_BPF_FLAG_ACT_DIRECT,
- },
- },
- };
-
- snprintf(req.options.name.str, sizeof(req.options.name.str), "%s:[*fsobj]",
- basename(pathname.c_str()));
-
- // The exception may be thrown from sendAndProcessNetlinkResponse. Close the file descriptor of
- // BPF program before returning the function in any case.
- sendAndProcessNetlinkResponse(env, &req, sizeof(req));
- close(bpfFd);
-}
-
-// tc filter del dev .. in/egress prio .. protocol ..
-static void com_android_networkstack_tethering_BpfUtils_tcFilterDelDev(JNIEnv* env, jobject clazz,
- jint ifIndex,
- jboolean ingress,
- jshort prio, jshort proto) {
- const struct {
- nlmsghdr n;
- tcmsg t;
- } req = {
- .n =
- {
- .nlmsg_len = sizeof(req),
- .nlmsg_type = RTM_DELTFILTER,
- .nlmsg_flags = NETLINK_REQUEST_FLAGS,
- },
- .t =
- {
- .tcm_family = AF_UNSPEC,
- .tcm_ifindex = ifIndex,
- .tcm_handle = TC_H_UNSPEC,
- .tcm_parent = TC_H_MAKE(TC_H_CLSACT,
- ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
- .tcm_info = static_cast<__u32>((static_cast<uint16_t>(prio) << 16) |
- htons(static_cast<uint16_t>(proto))),
- },
- };
-
- sendAndProcessNetlinkResponse(env, &req, sizeof(req));
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- {"isEthernet", "(Ljava/lang/String;)Z",
- (void*)com_android_networkstack_tethering_BpfUtils_isEthernet},
- {"tcFilterAddDevBpf", "(IZSSLjava/lang/String;)V",
- (void*)com_android_networkstack_tethering_BpfUtils_tcFilterAddDevBpf},
- {"tcFilterDelDev", "(IZSS)V",
- (void*)com_android_networkstack_tethering_BpfUtils_tcFilterDelDev},
-};
-
-int register_com_android_networkstack_tethering_BpfUtils(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "com/android/networkstack/tethering/BpfUtils", gMethods,
- NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/Tethering/jni/android_net_util_TetheringUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
similarity index 83%
rename from Tethering/jni/android_net_util_TetheringUtils.cpp
rename to Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
index 2e76501..291bf54 100644
--- a/Tethering/jni/android_net_util_TetheringUtils.cpp
+++ b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
@@ -38,7 +38,8 @@
jniThrowExceptionFmt(env, "java/net/SocketException", "%s: %s", msg, strerror(error));
}
-static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) {
+static void com_android_networkstack_tethering_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),
@@ -64,19 +65,18 @@
}
}
-static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd)
-{
- android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
+static void com_android_networkstack_tethering_util_setupNaSocket(JNIEnv *env, jobject clazz,
+ jobject javaFd) {
+ com_android_networkstack_tethering_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 com_android_networkstack_tethering_util_setupNsSocket(JNIEnv *env, jobject clazz,
+ jobject javaFd) {
+ com_android_networkstack_tethering_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
}
-static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
- jint ifIndex)
-{
+static void com_android_networkstack_tethering_util_setupRaSocket(JNIEnv *env, jobject clazz,
+ jobject javaFd, jint ifIndex) {
static const int kLinkLocalHopLimit = 255;
int fd = netjniutils::GetNativeFileDescriptor(env, javaFd);
@@ -164,16 +164,16 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "setupNaSocket", "(Ljava/io/FileDescriptor;)V",
- (void*) android_net_util_setupNaSocket },
+ (void*) com_android_networkstack_tethering_util_setupNaSocket },
{ "setupNsSocket", "(Ljava/io/FileDescriptor;)V",
- (void*) android_net_util_setupNsSocket },
+ (void*) com_android_networkstack_tethering_util_setupNsSocket },
{ "setupRaSocket", "(Ljava/io/FileDescriptor;I)V",
- (void*) android_net_util_setupRaSocket },
+ (void*) com_android_networkstack_tethering_util_setupRaSocket },
};
-int register_android_net_util_TetheringUtils(JNIEnv* env) {
+int register_com_android_networkstack_tethering_util_TetheringUtils(JNIEnv* env) {
return jniRegisterNativeMethods(env,
- "android/net/util/TetheringUtils",
+ "com/android/networkstack/tethering/util/TetheringUtils",
gMethods, NELEM(gMethods));
}
diff --git a/Tethering/jni/onload.cpp b/Tethering/jni/onload.cpp
index 02e602d..ed80128 100644
--- a/Tethering/jni/onload.cpp
+++ b/Tethering/jni/onload.cpp
@@ -22,10 +22,11 @@
namespace android {
-int register_android_net_util_TetheringUtils(JNIEnv* env);
-int register_com_android_networkstack_tethering_BpfMap(JNIEnv* env);
+int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name);
+int register_com_android_net_module_util_TcUtils(JNIEnv* env, char const* class_name);
int register_com_android_networkstack_tethering_BpfCoordinator(JNIEnv* env);
int register_com_android_networkstack_tethering_BpfUtils(JNIEnv* env);
+int register_com_android_networkstack_tethering_util_TetheringUtils(JNIEnv* env);
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv *env;
@@ -34,14 +35,16 @@
return JNI_ERR;
}
- if (register_android_net_util_TetheringUtils(env) < 0) return JNI_ERR;
+ if (register_com_android_networkstack_tethering_util_TetheringUtils(env) < 0) return JNI_ERR;
- if (register_com_android_networkstack_tethering_BpfMap(env) < 0) return JNI_ERR;
+ if (register_com_android_net_module_util_BpfMap(env,
+ "com/android/networkstack/tethering/util/BpfMap") < 0) return JNI_ERR;
+
+ if (register_com_android_net_module_util_TcUtils(env,
+ "com/android/networkstack/tethering/util/TcUtils") < 0) return JNI_ERR;
if (register_com_android_networkstack_tethering_BpfCoordinator(env) < 0) return JNI_ERR;
- if (register_com_android_networkstack_tethering_BpfUtils(env) < 0) return JNI_ERR;
-
return JNI_VERSION_1_6;
}
diff --git a/Tethering/proguard.flags b/Tethering/proguard.flags
index 75ecdce..6735317 100644
--- a/Tethering/proguard.flags
+++ b/Tethering/proguard.flags
@@ -4,7 +4,11 @@
static final int EVENT_*;
}
--keep class com.android.networkstack.tethering.BpfMap {
+-keep class com.android.networkstack.tethering.util.BpfMap {
+ native <methods>;
+}
+
+-keep class com.android.networkstack.tethering.util.TcUtils {
native <methods>;
}
diff --git a/Tethering/res/values-mcc204-mnc04-af/strings.xml b/Tethering/res/values-mcc204-mnc04-af/strings.xml
new file mode 100644
index 0000000..052ca09
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-af/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-am/strings.xml b/Tethering/res/values-mcc204-mnc04-am/strings.xml
new file mode 100644
index 0000000..0518c5a
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-am/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ar/strings.xml b/Tethering/res/values-mcc204-mnc04-ar/strings.xml
new file mode 100644
index 0000000..e6d8423
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ar/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-as/strings.xml b/Tethering/res/values-mcc204-mnc04-as/strings.xml
new file mode 100644
index 0000000..4c57f21
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-as/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-az/strings.xml b/Tethering/res/values-mcc204-mnc04-az/strings.xml
new file mode 100644
index 0000000..2610ab1
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-az/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml b/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..7b032ba
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-be/strings.xml b/Tethering/res/values-mcc204-mnc04-be/strings.xml
new file mode 100644
index 0000000..2362a1e
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-be/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-bg/strings.xml b/Tethering/res/values-mcc204-mnc04-bg/strings.xml
new file mode 100644
index 0000000..6ef1b0b
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-bg/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-bn/strings.xml b/Tethering/res/values-mcc204-mnc04-bn/strings.xml
new file mode 100644
index 0000000..9a3033c
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-bn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-bs/strings.xml b/Tethering/res/values-mcc204-mnc04-bs/strings.xml
new file mode 100644
index 0000000..57f6d88
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-bs/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ca/strings.xml b/Tethering/res/values-mcc204-mnc04-ca/strings.xml
new file mode 100644
index 0000000..e3ad666
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ca/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-cs/strings.xml b/Tethering/res/values-mcc204-mnc04-cs/strings.xml
new file mode 100644
index 0000000..f099281
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-cs/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-da/strings.xml b/Tethering/res/values-mcc204-mnc04-da/strings.xml
new file mode 100644
index 0000000..1fb2374
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-da/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-de/strings.xml b/Tethering/res/values-mcc204-mnc04-de/strings.xml
new file mode 100644
index 0000000..56d1d1d
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-de/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-el/strings.xml b/Tethering/res/values-mcc204-mnc04-el/strings.xml
new file mode 100644
index 0000000..674f1f6
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-el/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml b/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml
new file mode 100644
index 0000000..3046a37
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml b/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml
new file mode 100644
index 0000000..3046a37
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml b/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml
new file mode 100644
index 0000000..3046a37
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml b/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml
new file mode 100644
index 0000000..3046a37
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml b/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml
new file mode 100644
index 0000000..20c9b94
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml b/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml
new file mode 100644
index 0000000..956547c
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-es/strings.xml b/Tethering/res/values-mcc204-mnc04-es/strings.xml
new file mode 100644
index 0000000..831ec1f
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-es/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-et/strings.xml b/Tethering/res/values-mcc204-mnc04-et/strings.xml
new file mode 100644
index 0000000..ff8dde5
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-et/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-eu/strings.xml b/Tethering/res/values-mcc204-mnc04-eu/strings.xml
new file mode 100644
index 0000000..c4f70a3
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-eu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-fa/strings.xml b/Tethering/res/values-mcc204-mnc04-fa/strings.xml
new file mode 100644
index 0000000..79e3ef1
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-fa/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-fi/strings.xml b/Tethering/res/values-mcc204-mnc04-fi/strings.xml
new file mode 100644
index 0000000..64921bc
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-fi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml b/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml
new file mode 100644
index 0000000..eda7b59
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-fr/strings.xml b/Tethering/res/values-mcc204-mnc04-fr/strings.xml
new file mode 100644
index 0000000..eda7b59
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-fr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-gl/strings.xml b/Tethering/res/values-mcc204-mnc04-gl/strings.xml
new file mode 100644
index 0000000..c163c61
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-gl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-gu/strings.xml b/Tethering/res/values-mcc204-mnc04-gu/strings.xml
new file mode 100644
index 0000000..796d42e
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-gu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-hi/strings.xml b/Tethering/res/values-mcc204-mnc04-hi/strings.xml
new file mode 100644
index 0000000..a244200
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-hi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-hr/strings.xml b/Tethering/res/values-mcc204-mnc04-hr/strings.xml
new file mode 100644
index 0000000..41618af
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-hr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-hu/strings.xml b/Tethering/res/values-mcc204-mnc04-hu/strings.xml
new file mode 100644
index 0000000..39b7a69
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-hu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-hy/strings.xml b/Tethering/res/values-mcc204-mnc04-hy/strings.xml
new file mode 100644
index 0000000..c14ae10
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-hy/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-in/strings.xml b/Tethering/res/values-mcc204-mnc04-in/strings.xml
new file mode 100644
index 0000000..1243d22
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-in/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-is/strings.xml b/Tethering/res/values-mcc204-mnc04-is/strings.xml
new file mode 100644
index 0000000..82a7d01
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-is/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-it/strings.xml b/Tethering/res/values-mcc204-mnc04-it/strings.xml
new file mode 100644
index 0000000..a0f52dc
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-it/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-iw/strings.xml b/Tethering/res/values-mcc204-mnc04-iw/strings.xml
new file mode 100644
index 0000000..80807bc
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-iw/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ja/strings.xml b/Tethering/res/values-mcc204-mnc04-ja/strings.xml
new file mode 100644
index 0000000..0e21a7f
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ja/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ka/strings.xml b/Tethering/res/values-mcc204-mnc04-ka/strings.xml
new file mode 100644
index 0000000..6d3b548
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ka/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-kk/strings.xml b/Tethering/res/values-mcc204-mnc04-kk/strings.xml
new file mode 100644
index 0000000..985fc3f
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-kk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-km/strings.xml b/Tethering/res/values-mcc204-mnc04-km/strings.xml
new file mode 100644
index 0000000..03b5cb6
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-km/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-kn/strings.xml b/Tethering/res/values-mcc204-mnc04-kn/strings.xml
new file mode 100644
index 0000000..f0adad8
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-kn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ko/strings.xml b/Tethering/res/values-mcc204-mnc04-ko/strings.xml
new file mode 100644
index 0000000..9218e9a
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ko/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ky/strings.xml b/Tethering/res/values-mcc204-mnc04-ky/strings.xml
new file mode 100644
index 0000000..35a060a
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ky/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-lo/strings.xml b/Tethering/res/values-mcc204-mnc04-lo/strings.xml
new file mode 100644
index 0000000..1d9203b
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-lo/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-lt/strings.xml b/Tethering/res/values-mcc204-mnc04-lt/strings.xml
new file mode 100644
index 0000000..db5178b
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-lt/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-lv/strings.xml b/Tethering/res/values-mcc204-mnc04-lv/strings.xml
new file mode 100644
index 0000000..c712173
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-lv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-mk/strings.xml b/Tethering/res/values-mcc204-mnc04-mk/strings.xml
new file mode 100644
index 0000000..aa44909
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-mk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ml/strings.xml b/Tethering/res/values-mcc204-mnc04-ml/strings.xml
new file mode 100644
index 0000000..d376fe5
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ml/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-mn/strings.xml b/Tethering/res/values-mcc204-mnc04-mn/strings.xml
new file mode 100644
index 0000000..417213f
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-mn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-mr/strings.xml b/Tethering/res/values-mcc204-mnc04-mr/strings.xml
new file mode 100644
index 0000000..2ed153fb1
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-mr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ms/strings.xml b/Tethering/res/values-mcc204-mnc04-ms/strings.xml
new file mode 100644
index 0000000..50817fd
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ms/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-my/strings.xml b/Tethering/res/values-mcc204-mnc04-my/strings.xml
new file mode 100644
index 0000000..c0d70e3
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-my/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-nb/strings.xml b/Tethering/res/values-mcc204-mnc04-nb/strings.xml
new file mode 100644
index 0000000..1e7f1c6
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-nb/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ne/strings.xml b/Tethering/res/values-mcc204-mnc04-ne/strings.xml
new file mode 100644
index 0000000..63ce155
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ne/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-nl/strings.xml b/Tethering/res/values-mcc204-mnc04-nl/strings.xml
new file mode 100644
index 0000000..bf14a0f
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-nl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-or/strings.xml b/Tethering/res/values-mcc204-mnc04-or/strings.xml
new file mode 100644
index 0000000..ab87b76
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-or/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-pa/strings.xml b/Tethering/res/values-mcc204-mnc04-pa/strings.xml
new file mode 100644
index 0000000..b09f285
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-pa/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-pl/strings.xml b/Tethering/res/values-mcc204-mnc04-pl/strings.xml
new file mode 100644
index 0000000..8becd07
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-pl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml b/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml
new file mode 100644
index 0000000..8e01736
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml b/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml
new file mode 100644
index 0000000..2356379
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-pt/strings.xml b/Tethering/res/values-mcc204-mnc04-pt/strings.xml
new file mode 100644
index 0000000..8e01736
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-pt/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ro/strings.xml b/Tethering/res/values-mcc204-mnc04-ro/strings.xml
new file mode 100644
index 0000000..2e62bd6
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ro/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ru/strings.xml b/Tethering/res/values-mcc204-mnc04-ru/strings.xml
new file mode 100644
index 0000000..a2b1640
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ru/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-si/strings.xml b/Tethering/res/values-mcc204-mnc04-si/strings.xml
new file mode 100644
index 0000000..632748a
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-si/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-sk/strings.xml b/Tethering/res/values-mcc204-mnc04-sk/strings.xml
new file mode 100644
index 0000000..247fc1b
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-sk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-sl/strings.xml b/Tethering/res/values-mcc204-mnc04-sl/strings.xml
new file mode 100644
index 0000000..ed22372
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-sl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-sq/strings.xml b/Tethering/res/values-mcc204-mnc04-sq/strings.xml
new file mode 100644
index 0000000..4bfab6e
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-sq/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-sr/strings.xml b/Tethering/res/values-mcc204-mnc04-sr/strings.xml
new file mode 100644
index 0000000..478d53a
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-sr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-sv/strings.xml b/Tethering/res/values-mcc204-mnc04-sv/strings.xml
new file mode 100644
index 0000000..a793ed6
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-sv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-sw/strings.xml b/Tethering/res/values-mcc204-mnc04-sw/strings.xml
new file mode 100644
index 0000000..18ee457
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-sw/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ta/strings.xml b/Tethering/res/values-mcc204-mnc04-ta/strings.xml
new file mode 100644
index 0000000..7eebd67
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ta/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-te/strings.xml b/Tethering/res/values-mcc204-mnc04-te/strings.xml
new file mode 100644
index 0000000..0986534
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-te/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-th/strings.xml b/Tethering/res/values-mcc204-mnc04-th/strings.xml
new file mode 100644
index 0000000..3837002
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-th/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-tl/strings.xml b/Tethering/res/values-mcc204-mnc04-tl/strings.xml
new file mode 100644
index 0000000..208f893
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-tl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-tr/strings.xml b/Tethering/res/values-mcc204-mnc04-tr/strings.xml
new file mode 100644
index 0000000..3482faf
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-tr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-uk/strings.xml b/Tethering/res/values-mcc204-mnc04-uk/strings.xml
new file mode 100644
index 0000000..dea3114
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-uk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-ur/strings.xml b/Tethering/res/values-mcc204-mnc04-ur/strings.xml
new file mode 100644
index 0000000..09bc0c9
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-ur/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-uz/strings.xml b/Tethering/res/values-mcc204-mnc04-uz/strings.xml
new file mode 100644
index 0000000..715d348
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-uz/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-vi/strings.xml b/Tethering/res/values-mcc204-mnc04-vi/strings.xml
new file mode 100644
index 0000000..bf4ee10
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-vi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml b/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml
new file mode 100644
index 0000000..cdb4224
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml b/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml
new file mode 100644
index 0000000..3bb52e4
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml b/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml
new file mode 100644
index 0000000..298c3ea
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/res/values-mcc204-mnc04-zu/strings.xml b/Tethering/res/values-mcc204-mnc04-zu/strings.xml
new file mode 100644
index 0000000..3dc0078
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04-zu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under 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/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index cf49683..2bb19db 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -21,12 +21,12 @@
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 static com.android.networkstack.tethering.UpstreamNetworkState.isVcnInterface;
+import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
+import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_IPSERVER;
import android.net.INetd;
import android.net.INetworkStackStatusCallback;
@@ -46,11 +46,7 @@
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.Handler;
import android.os.Looper;
@@ -67,10 +63,13 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.NetdUtils;
import com.android.networkstack.tethering.BpfCoordinator;
import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
+import com.android.networkstack.tethering.util.InterfaceSet;
+import com.android.networkstack.tethering.util.PrefixUtils;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -615,10 +614,8 @@
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.
+ if (shouldNotConfigureBluetoothInterface()) {
+ // Interface was already configured elsewhere, only start DHCP.
return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
}
@@ -652,12 +649,15 @@
return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
}
+ private boolean shouldNotConfigureBluetoothInterface() {
+ // Before T, bluetooth tethering configures the interface elsewhere.
+ return (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) && !SdkLevel.isAtLeastT();
+ }
+
private LinkAddress requestIpv4Address(final boolean useLastAddress) {
if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
- if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
- return new LinkAddress(BLUETOOTH_IFACE_ADDR);
- }
+ if (shouldNotConfigureBluetoothInterface()) return new LinkAddress(BLUETOOTH_IFACE_ADDR);
return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
}
@@ -767,7 +767,7 @@
}
private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
- final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
+ final int removalFailures = NetdUtils.removeRoutesFromLocalNetwork(
mNetd, toBeRemoved);
if (removalFailures > 0) {
mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
@@ -785,7 +785,7 @@
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);
+ NetdUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
} catch (IllegalStateException e) {
mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
return;
diff --git a/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/Tethering/src/android/net/ip/NeighborPacketForwarder.java
index 084743d..27e59a1 100644
--- a/Tethering/src/android/net/ip/NeighborPacketForwarder.java
+++ b/Tethering/src/android/net/ip/NeighborPacketForwarder.java
@@ -26,13 +26,13 @@
import android.net.util.InterfaceParams;
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 com.android.net.module.util.PacketReader;
+import com.android.networkstack.tethering.util.TetheringUtils;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 543a5c7..74f9369 100644
--- a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -17,7 +17,6 @@
package android.net.ip;
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;
@@ -32,6 +31,7 @@
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
import static com.android.net.module.util.NetworkStackConstants.TAG_SYSTEM_NEIGHBOR;
+import static com.android.networkstack.tethering.util.TetheringUtils.getAllNodesForScopeId;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -39,7 +39,6 @@
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;
@@ -52,6 +51,7 @@
import com.android.net.module.util.structs.PrefixInformationOption;
import com.android.net.module.util.structs.RaHeader;
import com.android.net.module.util.structs.RdnssOption;
+import com.android.networkstack.tethering.util.TetheringUtils;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 1df3e58..8c8a2fd 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -32,6 +32,7 @@
import static com.android.networkstack.tethering.BpfUtils.UPSTREAM;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
import static com.android.networkstack.tethering.UpstreamNetworkState.isVcnInterface;
+import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
import android.app.usage.NetworkStatsManager;
import android.net.INetd;
@@ -45,7 +46,6 @@
import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
-import android.net.util.TetheringUtils.ForwardedStats;
import android.os.Handler;
import android.os.SystemClock;
import android.system.ErrnoException;
@@ -61,6 +61,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.BpfMap;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.Struct;
@@ -68,6 +69,7 @@
import com.android.net.module.util.netlink.NetlinkConstants;
import com.android.net.module.util.netlink.NetlinkSocket;
import com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim;
+import com.android.networkstack.tethering.util.TetheringUtils.ForwardedStats;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -98,7 +100,7 @@
// TetherService, but for tests it needs to be either loaded here or loaded by every test.
// TODO: is there a better way?
static {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
}
private static final String TAG = BpfCoordinator.class.getSimpleName();
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfMap.java b/Tethering/src/com/android/networkstack/tethering/BpfMap.java
deleted file mode 100644
index 1363dc5..0000000
--- a/Tethering/src/com/android/networkstack/tethering/BpfMap.java
+++ /dev/null
@@ -1,288 +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.system.OsConstants.EEXIST;
-import static android.system.OsConstants.ENOENT;
-
-import android.system.ErrnoException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.net.module.util.Struct;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.function.BiConsumer;
-
-/**
- * BpfMap is a key -> value mapping structure that is designed to maintained the bpf map entries.
- * This is a wrapper class of in-kernel data structure. The in-kernel data can be read/written by
- * passing syscalls with map file descriptor.
- *
- * @param <K> the key of the map.
- * @param <V> the value of the map.
- */
-public class BpfMap<K extends Struct, V extends Struct> implements AutoCloseable {
- static {
- System.loadLibrary("tetherutilsjni");
- }
-
- // Following definitions from kernel include/uapi/linux/bpf.h
- public static final int BPF_F_RDWR = 0;
- public static final int BPF_F_RDONLY = 1 << 3;
- public static final int BPF_F_WRONLY = 1 << 4;
-
- public static final int BPF_MAP_TYPE_HASH = 1;
-
- private static final int BPF_F_NO_PREALLOC = 1;
-
- private static final int BPF_ANY = 0;
- private static final int BPF_NOEXIST = 1;
- private static final int BPF_EXIST = 2;
-
- private final int mMapFd;
- private final Class<K> mKeyClass;
- private final Class<V> mValueClass;
- private final int mKeySize;
- private final int mValueSize;
-
- /**
- * Create a BpfMap map wrapper with "path" of filesystem.
- *
- * @param flag the access mode, one of BPF_F_RDWR, BPF_F_RDONLY, or BPF_F_WRONLY.
- * @throws ErrnoException if the BPF map associated with {@code path} cannot be retrieved.
- * @throws NullPointerException if {@code path} is null.
- */
- public BpfMap(@NonNull final String path, final int flag, final Class<K> key,
- final Class<V> value) throws ErrnoException, NullPointerException {
- mMapFd = bpfFdGet(path, flag);
-
- mKeyClass = key;
- mValueClass = value;
- mKeySize = Struct.getSize(key);
- mValueSize = Struct.getSize(value);
- }
-
- /**
- * Constructor for testing only.
- * The derived class implements an internal mocked map. It need to implement all functions
- * which are related with the native BPF map because the BPF map handler is not initialized.
- * See BpfCoordinatorTest#TestBpfMap.
- */
- @VisibleForTesting
- protected BpfMap(final Class<K> key, final Class<V> value) {
- mMapFd = -1;
- mKeyClass = key;
- mValueClass = value;
- mKeySize = Struct.getSize(key);
- mValueSize = Struct.getSize(value);
- }
-
- /**
- * Update an existing or create a new key -> value entry in an eBbpf map.
- * (use insertOrReplaceEntry() if you need to know whether insert or replace happened)
- */
- public void updateEntry(K key, V value) throws ErrnoException {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_ANY);
- }
-
- /**
- * If the key does not exist in the map, insert key -> value entry into eBpf map.
- * Otherwise IllegalStateException will be thrown.
- */
- public void insertEntry(K key, V value)
- throws ErrnoException, IllegalStateException {
- try {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_NOEXIST);
- } catch (ErrnoException e) {
- if (e.errno == EEXIST) throw new IllegalStateException(key + " already exists");
-
- throw e;
- }
- }
-
- /**
- * If the key already exists in the map, replace its value. Otherwise NoSuchElementException
- * will be thrown.
- */
- public void replaceEntry(K key, V value)
- throws ErrnoException, NoSuchElementException {
- try {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_EXIST);
- } catch (ErrnoException e) {
- if (e.errno == ENOENT) throw new NoSuchElementException(key + " not found");
-
- throw e;
- }
- }
-
- /**
- * Update an existing or create a new key -> value entry in an eBbpf map.
- * Returns true if inserted, false if replaced.
- * (use updateEntry() if you don't care whether insert or replace happened)
- * Note: see inline comment below if running concurrently with delete operations.
- */
- public boolean insertOrReplaceEntry(K key, V value)
- throws ErrnoException {
- try {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_NOEXIST);
- return true; /* insert succeeded */
- } catch (ErrnoException e) {
- if (e.errno != EEXIST) throw e;
- }
- try {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_EXIST);
- return false; /* replace succeeded */
- } catch (ErrnoException e) {
- if (e.errno != ENOENT) throw e;
- }
- /* If we reach here somebody deleted after our insert attempt and before our replace:
- * this implies a race happened. The kernel bpf delete interface only takes a key,
- * and not the value, so we can safely pretend the replace actually succeeded and
- * was immediately followed by the other thread's delete, since the delete cannot
- * observe the potential change to the value.
- */
- return false; /* pretend replace succeeded */
- }
-
- /** Remove existing key from eBpf map. Return false if map was not modified. */
- public boolean deleteEntry(K key) throws ErrnoException {
- return deleteMapEntry(mMapFd, key.writeToBytes());
- }
-
- /** Returns {@code true} if this map contains no elements. */
- public boolean isEmpty() throws ErrnoException {
- return getFirstKey() == null;
- }
-
- private K getNextKeyInternal(@Nullable K key) throws ErrnoException {
- final byte[] rawKey = getNextRawKey(
- key == null ? null : key.writeToBytes());
- if (rawKey == null) return null;
-
- final ByteBuffer buffer = ByteBuffer.wrap(rawKey);
- buffer.order(ByteOrder.nativeOrder());
- return Struct.parse(mKeyClass, buffer);
- }
-
- /**
- * Get the next key of the passed-in key. If the passed-in key is not found, return the first
- * key. If the passed-in key is the last one, return null.
- *
- * TODO: consider allowing null passed-in key.
- */
- public K getNextKey(@NonNull K key) throws ErrnoException {
- Objects.requireNonNull(key);
- return getNextKeyInternal(key);
- }
-
- private byte[] getNextRawKey(@Nullable final byte[] key) throws ErrnoException {
- byte[] nextKey = new byte[mKeySize];
- if (getNextMapKey(mMapFd, key, nextKey)) return nextKey;
-
- return null;
- }
-
- /** Get the first key of eBpf map. */
- public K getFirstKey() throws ErrnoException {
- return getNextKeyInternal(null);
- }
-
- /** Check whether a key exists in the map. */
- public boolean containsKey(@NonNull K key) throws ErrnoException {
- Objects.requireNonNull(key);
-
- final byte[] rawValue = getRawValue(key.writeToBytes());
- return rawValue != null;
- }
-
- /** Retrieve a value from the map. Return null if there is no such key. */
- public V getValue(@NonNull K key) throws ErrnoException {
- Objects.requireNonNull(key);
- final byte[] rawValue = getRawValue(key.writeToBytes());
-
- if (rawValue == null) return null;
-
- final ByteBuffer buffer = ByteBuffer.wrap(rawValue);
- buffer.order(ByteOrder.nativeOrder());
- return Struct.parse(mValueClass, buffer);
- }
-
- private byte[] getRawValue(final byte[] key) throws ErrnoException {
- byte[] value = new byte[mValueSize];
- if (findMapEntry(mMapFd, key, value)) return value;
-
- return null;
- }
-
- /**
- * Iterate through the map and handle each key -> value retrieved base on the given BiConsumer.
- * The given BiConsumer may to delete the passed-in entry, but is not allowed to perform any
- * other structural modifications to the map, such as adding entries or deleting other entries.
- * Otherwise, iteration will result in undefined behaviour.
- */
- public void forEach(BiConsumer<K, V> action) throws ErrnoException {
- @Nullable K nextKey = getFirstKey();
-
- while (nextKey != null) {
- @NonNull final K curKey = nextKey;
- @NonNull final V value = getValue(curKey);
-
- nextKey = getNextKey(curKey);
- action.accept(curKey, value);
- }
- }
-
- @Override
- public void close() throws ErrnoException {
- closeMap(mMapFd);
- }
-
- /**
- * Clears the map. The map may already be empty.
- *
- * @throws ErrnoException if the map is already closed, if an error occurred during iteration,
- * or if a non-ENOENT error occurred when deleting a key.
- */
- public void clear() throws ErrnoException {
- K key = getFirstKey();
- while (key != null) {
- deleteEntry(key); // ignores ENOENT.
- key = getFirstKey();
- }
- }
-
- private static native int closeMap(int fd) throws ErrnoException;
-
- private native int bpfFdGet(String path, int mode) throws ErrnoException, NullPointerException;
-
- private native void writeToMapEntry(int fd, byte[] key, byte[] value, int flags)
- throws ErrnoException;
-
- private native boolean deleteMapEntry(int fd, byte[] key) throws ErrnoException;
-
- // If key is found, the operation returns true and the nextKey would reference to the next
- // element. If key is not found, the operation returns true and the nextKey would reference to
- // the first element. If key is the last element, false is returned.
- private native boolean getNextMapKey(int fd, byte[] key, byte[] nextKey) throws ErrnoException;
-
- private native boolean findMapEntry(int fd, byte[] key, byte[] value) throws ErrnoException;
-}
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
index cd6733a..77efb51 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
@@ -18,10 +18,14 @@
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
+import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
+
import android.net.util.InterfaceParams;
import androidx.annotation.NonNull;
+import com.android.net.module.util.TcUtils;
+
import java.io.IOException;
/**
@@ -31,7 +35,7 @@
*/
public class BpfUtils {
static {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
}
// For better code clarity when used for 'bool ingress' parameter.
@@ -80,7 +84,7 @@
boolean ether;
try {
- ether = isEthernet(iface);
+ ether = TcUtils.isEthernet(iface);
} catch (IOException e) {
throw new IOException("isEthernet(" + params.index + "[" + iface + "]) failure: " + e);
}
@@ -88,7 +92,7 @@
try {
// tc filter add dev .. ingress prio 1 protocol ipv6 bpf object-pinned /sys/fs/bpf/...
// direct-action
- tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6,
+ TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6,
makeProgPath(downstream, 6, ether));
} catch (IOException e) {
throw new IOException("tc filter add dev (" + params.index + "[" + iface
@@ -98,7 +102,7 @@
try {
// tc filter add dev .. ingress prio 2 protocol ip bpf object-pinned /sys/fs/bpf/...
// direct-action
- tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP,
+ TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP,
makeProgPath(downstream, 4, ether));
} catch (IOException e) {
throw new IOException("tc filter add dev (" + params.index + "[" + iface
@@ -119,7 +123,7 @@
try {
// tc filter del dev .. ingress prio 1 protocol ipv6
- tcFilterDelDev(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6);
+ TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6);
} catch (IOException e) {
throw new IOException("tc filter del dev (" + params.index + "[" + iface
+ "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e);
@@ -127,18 +131,10 @@
try {
// tc filter del dev .. ingress prio 2 protocol ip
- tcFilterDelDev(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP);
+ TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP);
} catch (IOException e) {
throw new IOException("tc filter del dev (" + params.index + "[" + iface
+ "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e);
}
}
-
- private static native boolean isEthernet(String iface) throws IOException;
-
- private static native void tcFilterAddDevBpf(int ifIndex, boolean ingress, short prio,
- short proto, String bpfProgPath) throws IOException;
-
- private static native void tcFilterDelDev(int ifIndex, boolean ingress, short prio,
- short proto) throws IOException;
}
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index f336d9b..9da66d8 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -16,10 +16,9 @@
package com.android.networkstack.tethering;
-import static android.net.util.TetheringUtils.uint16;
-
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static com.android.networkstack.tethering.util.TetheringUtils.uint16;
import android.annotation.IntDef;
import android.annotation.NonNull;
diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 4f616cd..c1e7127 100644
--- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -18,11 +18,11 @@
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 com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
import static java.util.Arrays.asList;
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 08170f9..db9a64f 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -52,7 +52,6 @@
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
import static android.net.TetheringManager.toIfaces;
-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;
@@ -67,6 +66,7 @@
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.isCellular;
+import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_MAIN_SM;
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
@@ -100,11 +100,7 @@
import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
import android.net.shared.NetdUtils;
-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;
@@ -127,6 +123,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -137,7 +134,16 @@
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.networkstack.apishim.common.BluetoothPanShim;
+import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
+import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
+import com.android.networkstack.tethering.util.InterfaceSet;
+import com.android.networkstack.tethering.util.PrefixUtils;
+import com.android.networkstack.tethering.util.TetheringUtils;
+import com.android.networkstack.tethering.util.VersionedBroadcastListener;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -264,9 +270,15 @@
private int mOffloadStatus = TETHER_HARDWARE_OFFLOAD_STOPPED;
private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest;
+ private TetheredInterfaceRequestShim mBluetoothIfaceRequest;
private String mConfiguredEthernetIface;
+ private String mConfiguredBluetoothIface;
private EthernetCallback mEthernetCallback;
+ private TetheredInterfaceCallbackShim mBluetoothCallback;
private SettingsObserver mSettingsObserver;
+ private BluetoothPan mBluetoothPan;
+ private PanServiceListener mBluetoothPanListener;
+ private ArrayList<Pair<Boolean, IIntResultListener>> mPendingPanRequests;
public Tethering(TetheringDependencies deps) {
mLog.mark("Tethering.constructed");
@@ -276,6 +288,11 @@
mLooper = mDeps.getTetheringLooper();
mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
+ // This is intended to ensrure that if something calls startTethering(bluetooth) just after
+ // bluetooth is enabled. Before onServiceConnected is called, store the calls into this
+ // list and handle them as soon as onServiceConnected is called.
+ mPendingPanRequests = new ArrayList<>();
+
mTetherStates = new ArrayMap<>();
mConnectedClientsTracker = new ConnectedClientsTracker();
@@ -524,14 +541,16 @@
}
}
- // This method needs to exist because TETHERING_BLUETOOTH and TETHERING_WIGIG can't use
- // enableIpServing.
+ // This method needs to exist because TETHERING_BLUETOOTH before Android T and TETHERING_WIGIG
+ // can't use enableIpServing.
private void processInterfaceStateChange(final String iface, boolean enabled) {
// Do not listen to USB interface state changes or USB interface add/removes. USB tethering
// is driven only by USB_ACTION broadcasts.
final int type = ifaceNameToType(iface);
if (type == TETHERING_USB || type == TETHERING_NCM) return;
+ if (type == TETHERING_BLUETOOTH && SdkLevel.isAtLeastT()) return;
+
if (enabled) {
ensureIpServerStarted(iface);
} else {
@@ -701,35 +720,151 @@
return;
}
- adapter.getProfileProxy(mContext, new ServiceListener() {
- @Override
- public void onServiceDisconnected(int profile) { }
+ if (mBluetoothPanListener != null && mBluetoothPanListener.isConnected()) {
+ // The PAN service is connected. Enable or disable bluetooth tethering.
+ // When bluetooth tethering is enabled, any time a PAN client pairs with this
+ // host, bluetooth will bring up a bt-pan interface and notify tethering to
+ // enable IP serving.
+ setBluetoothTetheringSettings(mBluetoothPan, enable, listener);
+ return;
+ }
- @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);
+ // The reference of IIntResultListener should only exist when application want to start
+ // tethering but tethering is not bound to pan service yet. Even if the calling process
+ // dies, the referenice of IIntResultListener would still keep in mPendingPanRequests. Once
+ // tethering bound to pan service (onServiceConnected) or bluetooth just crash
+ // (onServiceDisconnected), all the references from mPendingPanRequests would be cleared.
+ mPendingPanRequests.add(new Pair(enable, listener));
+
+ // Bluetooth tethering is not a popular feature. To avoid bind to bluetooth pan service all
+ // the time but user never use bluetooth tethering. mBluetoothPanListener is created first
+ // time someone calls a bluetooth tethering method (even if it's just to disable tethering
+ // when it's already disabled) and never unset after that.
+ if (mBluetoothPanListener == null) {
+ mBluetoothPanListener = new PanServiceListener();
+ adapter.getProfileProxy(mContext, mBluetoothPanListener, BluetoothProfile.PAN);
+ }
+ }
+
+ private class PanServiceListener implements ServiceListener {
+ private boolean mIsConnected = false;
+
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ // Posting this to handling onServiceConnected in tethering handler thread may have
+ // race condition that bluetooth service may disconnected when tethering thread
+ // actaully handle onServiceconnected. If this race happen, calling
+ // BluetoothPan#setBluetoothTethering would silently fail. It is fine because pan
+ // service is unreachable and both bluetooth and bluetooth tethering settings are off.
+ mHandler.post(() -> {
+ mBluetoothPan = (BluetoothPan) proxy;
+ mIsConnected = true;
+
+ for (Pair<Boolean, IIntResultListener> request : mPendingPanRequests) {
+ setBluetoothTetheringSettings(mBluetoothPan, request.first, request.second);
}
- // 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);
+ mPendingPanRequests.clear();
+ });
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ mHandler.post(() -> {
+ // onServiceDisconnected means Bluetooth is off (or crashed) and is not
+ // reachable before next onServiceConnected.
+ mIsConnected = false;
+
+ for (Pair<Boolean, IIntResultListener> request : mPendingPanRequests) {
+ sendTetherResult(request.second, TETHER_ERROR_SERVICE_UNAVAIL,
+ TETHERING_BLUETOOTH);
+ }
+ mPendingPanRequests.clear();
+ mBluetoothIfaceRequest = null;
+ mBluetoothCallback = null;
+ maybeDisableBluetoothIpServing();
+ });
+ }
+
+ public boolean isConnected() {
+ return mIsConnected;
+ }
+ }
+
+ private void setBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
+ final boolean enable, final IIntResultListener listener) {
+ if (SdkLevel.isAtLeastT()) {
+ changeBluetoothTetheringSettings(bluetoothPan, enable);
+ } else {
+ changeBluetoothTetheringSettingsPreT(bluetoothPan, enable);
+ }
+
+ // Enabling bluetooth tethering settings can silently fail. Send internal error if the
+ // result is not expected.
+ final int result = bluetoothPan.isTetheringOn() == enable
+ ? TETHER_ERROR_NO_ERROR : TETHER_ERROR_INTERNAL_ERROR;
+ sendTetherResult(listener, result, TETHERING_BLUETOOTH);
+ }
+
+ private void changeBluetoothTetheringSettingsPreT(@NonNull final BluetoothPan bluetoothPan,
+ final boolean enable) {
+ bluetoothPan.setBluetoothTethering(enable);
+ }
+
+ private void changeBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
+ final boolean enable) {
+ final BluetoothPanShim panShim = mDeps.getBluetoothPanShim(bluetoothPan);
+ if (enable) {
+ if (mBluetoothIfaceRequest != null) {
+ Log.d(TAG, "Bluetooth tethering settings already enabled");
+ return;
}
- }, BluetoothProfile.PAN);
+
+ mBluetoothCallback = new BluetoothCallback();
+ try {
+ mBluetoothIfaceRequest = panShim.requestTetheredInterface(mExecutor,
+ mBluetoothCallback);
+ } catch (UnsupportedApiLevelException e) {
+ Log.wtf(TAG, "Use unsupported API, " + e);
+ }
+ } else {
+ if (mBluetoothIfaceRequest == null) {
+ Log.d(TAG, "Bluetooth tethering settings already disabled");
+ return;
+ }
+
+ mBluetoothIfaceRequest.release();
+ mBluetoothIfaceRequest = null;
+ mBluetoothCallback = null;
+ // If bluetooth request is released, tethering won't able to receive
+ // onUnavailable callback, explicitly disable bluetooth IpServer manually.
+ maybeDisableBluetoothIpServing();
+ }
+ }
+
+ // BluetoothCallback is only called after T. Before T, PanService would call tether/untether to
+ // notify bluetooth interface status.
+ private class BluetoothCallback implements TetheredInterfaceCallbackShim {
+ @Override
+ public void onAvailable(String iface) {
+ if (this != mBluetoothCallback) return;
+
+ enableIpServing(TETHERING_BLUETOOTH, iface, getRequestedState(TETHERING_BLUETOOTH));
+ mConfiguredBluetoothIface = iface;
+ }
+
+ @Override
+ public void onUnavailable() {
+ if (this != mBluetoothCallback) return;
+
+ maybeDisableBluetoothIpServing();
+ }
+ }
+
+ private void maybeDisableBluetoothIpServing() {
+ if (mConfiguredBluetoothIface == null) return;
+
+ ensureIpServerStopped(mConfiguredBluetoothIface);
+ mConfiguredBluetoothIface = null;
}
private int setEthernetTethering(final boolean enable) {
@@ -1140,9 +1275,7 @@
final WifiP2pGroup group =
(WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
- if (VDBG) {
- Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group);
- }
+ mLog.i("WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group);
// if no group is formed, bring it down if needed.
if (p2pInfo == null || !p2pInfo.groupFormed) {
@@ -2601,4 +2734,13 @@
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
+
+ void setPreferTestNetworks(final boolean prefer, IIntResultListener listener) {
+ mHandler.post(() -> {
+ mUpstreamNetworkMonitor.setPreferTestNetworks(prefer);
+ try {
+ listener.onResult(TETHER_ERROR_NO_ERROR);
+ } catch (RemoteException e) { }
+ });
+ }
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index b6240c4..dd0d567 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -23,6 +23,8 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
+
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -60,8 +62,6 @@
private static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static final String TETHERING_MODULE_NAME = "com.android.tethering";
-
// 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
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 7df9475..c1a747e 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -18,6 +18,7 @@
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothPan;
import android.content.Context;
import android.net.INetd;
import android.net.ip.IpServer;
@@ -31,6 +32,8 @@
import androidx.annotation.NonNull;
import com.android.internal.util.StateMachine;
+import com.android.networkstack.apishim.BluetoothPanShimImpl;
+import com.android.networkstack.apishim.common.BluetoothPanShim;
import java.util.ArrayList;
@@ -158,4 +161,13 @@
TetheringConfiguration cfg) {
return new PrivateAddressCoordinator(ctx, cfg);
}
+
+ /**
+ * Get BluetoothPanShim object to enable/disable bluetooth tethering.
+ *
+ * TODO: use BluetoothPan directly when mainline module is built with API 32.
+ */
+ public BluetoothPanShim getBluetoothPanShim(BluetoothPan pan) {
+ return BluetoothPanShimImpl.newInstance(pan);
+ }
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java b/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
index ff38f71..3974fa5 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
@@ -16,13 +16,17 @@
package com.android.networkstack.tethering;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+
+import android.annotation.NonNull;
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 com.android.networkstack.tethering.util.InterfaceSet;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -78,13 +82,17 @@
// Minimal amount of IPv6 provisioning:
&& ns.linkProperties.hasGlobalIpv6Address()
// Temporary approximation of "dedicated prefix":
- && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+ && allowIpv6Tethering(ns.networkCapabilities);
return canTether
? getInterfaceForDestination(ns.linkProperties, IN6ADDR_ANY)
: null;
}
+ private static boolean allowIpv6Tethering(@NonNull final NetworkCapabilities nc) {
+ return nc.hasTransport(TRANSPORT_CELLULAR) || nc.hasTransport(TRANSPORT_TEST);
+ }
+
private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
final RouteInfo ri = (lp != null)
? NetUtils.selectBestRoute(lp.getAllRoutes(), dst)
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index 5ab3401..9fb61fe 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -17,6 +17,7 @@
package com.android.networkstack.tethering;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -46,13 +47,14 @@
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 com.android.networkstack.apishim.SettingsShimImpl;
+import com.android.networkstack.apishim.common.SettingsShim;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -66,6 +68,7 @@
private static final String TAG = TetheringService.class.getSimpleName();
private TetheringConnector mConnector;
+ private SettingsShim mSettingsShim;
@Override
public void onCreate() {
@@ -73,6 +76,8 @@
// 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);
+
+ mSettingsShim = SettingsShimImpl.newInstance();
}
/**
@@ -200,6 +205,18 @@
}
@Override
+ public void setPreferTestNetworks(boolean prefer, IIntResultListener listener) {
+ if (!checkCallingOrSelfPermission(NETWORK_SETTINGS)) {
+ try {
+ listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ } catch (RemoteException e) { }
+ return;
+ }
+
+ mTethering.setPreferTestNetworks(prefer, listener);
+ }
+
+ @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
mTethering.dump(fd, writer, args);
@@ -294,8 +311,8 @@
boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
@NonNull String callingPackage, @Nullable String callingAttributionTag,
boolean throwException) {
- return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
- throwException);
+ return mSettingsShim.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
+ callingAttributionTag, throwException);
}
/**
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 5584db2..9d19335 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -36,7 +36,6 @@
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;
@@ -49,6 +48,7 @@
import com.android.internal.util.StateMachine;
import com.android.networkstack.apishim.ConnectivityManagerShimImpl;
import com.android.networkstack.apishim.common.ConnectivityManagerShim;
+import com.android.networkstack.tethering.util.PrefixUtils;
import java.util.HashMap;
import java.util.HashSet;
@@ -135,6 +135,7 @@
private Network mDefaultInternetNetwork;
// The current upstream network used for tethering.
private Network mTetheringUpstreamNetwork;
+ private boolean mPreferTestNetworks;
public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
mContext = ctx;
@@ -325,6 +326,11 @@
final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
? mNetworkMap.get(mDefaultInternetNetwork)
: null;
+ if (mPreferTestNetworks) {
+ final UpstreamNetworkState testState = findFirstTestNetwork(mNetworkMap.values());
+ if (testState != null) return testState;
+ }
+
if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
if (!isCellularUpstreamPermitted()) return null;
@@ -656,6 +662,20 @@
return null;
}
+ static boolean isTestNetwork(UpstreamNetworkState ns) {
+ return ((ns != null) && (ns.networkCapabilities != null)
+ && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_TEST));
+ }
+
+ private UpstreamNetworkState findFirstTestNetwork(
+ Iterable<UpstreamNetworkState> netStates) {
+ for (UpstreamNetworkState ns : netStates) {
+ if (isTestNetwork(ns)) 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.
@@ -681,4 +701,9 @@
}
return builder.build();
}
+
+ /** Set test network as preferred upstream. */
+ public void setPreferTestNetworks(boolean prefer) {
+ mPreferTestNetworks = prefer;
+ }
}
diff --git a/Tethering/src/android/net/util/InterfaceSet.java b/Tethering/src/com/android/networkstack/tethering/util/InterfaceSet.java
similarity index 96%
rename from Tethering/src/android/net/util/InterfaceSet.java
rename to Tethering/src/com/android/networkstack/tethering/util/InterfaceSet.java
index 7589787..44573f8 100644
--- a/Tethering/src/android/net/util/InterfaceSet.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/InterfaceSet.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import java.util.Collections;
import java.util.HashSet;
diff --git a/Tethering/src/android/net/util/PrefixUtils.java b/Tethering/src/com/android/networkstack/tethering/util/PrefixUtils.java
similarity index 96%
rename from Tethering/src/android/net/util/PrefixUtils.java
rename to Tethering/src/com/android/networkstack/tethering/util/PrefixUtils.java
index f203e99..50e5c4a 100644
--- a/Tethering/src/android/net/util/PrefixUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/PrefixUtils.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.util.NetworkConstants;
import java.net.Inet4Address;
import java.net.InetAddress;
diff --git a/Tethering/src/android/net/util/TetheringMessageBase.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringMessageBase.java
similarity index 94%
rename from Tethering/src/android/net/util/TetheringMessageBase.java
rename to Tethering/src/com/android/networkstack/tethering/util/TetheringMessageBase.java
index 29c0a81..27bb0f7 100644
--- a/Tethering/src/android/net/util/TetheringMessageBase.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringMessageBase.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
/**
* This class defines Message.what base addresses for various state machine.
diff --git a/Tethering/src/android/net/util/TetheringUtils.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
similarity index 94%
rename from Tethering/src/android/net/util/TetheringUtils.java
rename to Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
index 29900d9..66d67a1 100644
--- a/Tethering/src/android/net/util/TetheringUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import android.net.TetherStatsParcel;
import android.net.TetheringRequestParcel;
@@ -21,6 +21,7 @@
import androidx.annotation.NonNull;
+import com.android.net.module.util.JniUtil;
import com.android.networkstack.tethering.TetherStatsValue;
import java.io.FileDescriptor;
@@ -37,13 +38,18 @@
*/
public class TetheringUtils {
static {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
}
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
};
+ /** The name should be com_android_networkstack_tethering_util_jni. */
+ public static String getTetheringJniLibraryName() {
+ return JniUtil.getJniLibraryName(TetheringUtils.class.getPackage());
+ }
+
/**
* Configures a socket for receiving and sending ICMPv6 neighbor advertisments.
* @param fd the socket's {@link FileDescriptor}.
diff --git a/Tethering/src/android/net/util/VersionedBroadcastListener.java b/Tethering/src/com/android/networkstack/tethering/util/VersionedBroadcastListener.java
similarity index 98%
rename from Tethering/src/android/net/util/VersionedBroadcastListener.java
rename to Tethering/src/com/android/networkstack/tethering/util/VersionedBroadcastListener.java
index e2804ab..c9e75c0 100644
--- a/Tethering/src/android/net/util/VersionedBroadcastListener.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/VersionedBroadcastListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index 2593b1b..d2188d1 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -19,6 +19,7 @@
java_defaults {
name: "TetheringIntegrationTestsDefaults",
+ defaults: ["framework-connectivity-test-defaults"],
srcs: [
"src/**/*.java",
"src/**/*.kt",
@@ -47,7 +48,7 @@
// Use with NetworkStackJarJarRules.
android_library {
name: "TetheringIntegrationTestsLatestSdkLib",
- target_sdk_version: "30",
+ target_sdk_version: "31",
platform_apis: true,
defaults: ["TetheringIntegrationTestsDefaults"],
visibility: [
@@ -76,7 +77,7 @@
defaults: ["TetheringIntegrationTestsDefaults"],
test_suites: [
"device-tests",
- "mts",
+ "mts-tethering",
],
compile_multilib: "both",
jarjar_rules: ":NetworkStackJarJarRules",
@@ -127,8 +128,8 @@
name: "TetheringCoverageTests",
platform_apis: true,
min_sdk_version: "30",
- target_sdk_version: "30",
- test_suites: ["device-tests", "mts"],
+ target_sdk_version: "31",
+ test_suites: ["device-tests", "mts-tethering"],
test_config: "AndroidTest_Coverage.xml",
defaults: ["libnetworkstackutilsjni_deps"],
static_libs: [
@@ -141,7 +142,7 @@
"libstaticjvmtiagent",
// For NetworkStackUtils included in NetworkStackBase
"libnetworkstackutilsjni",
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
],
compile_multilib: "both",
manifest: "AndroidManifest_coverage.xml",
diff --git a/Tethering/tests/integration/AndroidManifest.xml b/Tethering/tests/integration/AndroidManifest.xml
index fddfaad..c89c556 100644
--- a/Tethering/tests/integration/AndroidManifest.xml
+++ b/Tethering/tests/integration/AndroidManifest.xml
@@ -17,6 +17,11 @@
package="com.android.networkstack.tethering.tests.integration">
<uses-permission android:name="android.permission.INTERNET"/>
+ <!-- The test need CHANGE_NETWORK_STATE permission to use requestNetwork API to setup test
+ network. Since R shell application don't have such permission, grant permission to the test
+ here. TODO: Remove CHANGE_NETWORK_STATE permission here and use adopt shell perssion to
+ obtain CHANGE_NETWORK_STATE for testing once R device is no longer supported. -->
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 26297a2..8bf1a2b 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -21,14 +21,21 @@
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.InetAddresses.parseNumericAddress;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringTester.RemoteResponder;
import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_IP;
+import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA;
+import static com.android.net.module.util.HexDump.dumpHexString;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -45,26 +52,29 @@
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.net.TetheringTester.TetheredDevice;
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.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.PacketBuilder;
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv6Header;
+import com.android.net.module.util.structs.Ipv4Header;
import com.android.net.module.util.structs.Ipv6Header;
+import com.android.net.module.util.structs.UdpHeader;
import com.android.testutils.HandlerUtils;
import com.android.testutils.TapPacketReader;
+import com.android.testutils.TestNetworkTracker;
import org.junit.After;
import org.junit.Before;
@@ -78,6 +88,7 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Random;
@@ -93,24 +104,23 @@
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 static final int TETHER_REACHABILITY_ATTEMPTS = 20;
+ private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8");
+ private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64");
+ private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8");
+ private static final InetAddress TEST_IP6_DNS = parseNumericAddress("2001:db8:1::888");
+ private static final ByteBuffer TEST_REACHABILITY_PAYLOAD =
+ ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa });
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 TestNetworkInterface mDownstreamIface;
private HandlerThread mHandlerThread;
private Handler mHandler;
- private TapPacketReader mTapPacketReader;
+ private TapPacketReader mDownstreamReader;
+ private TapPacketReader mUpstreamReader;
private TetheredInterfaceRequester mTetheredInterfaceRequester;
private MyTetheringEventCallback mTetheringEventCallback;
@@ -119,6 +129,8 @@
InstrumentationRegistry.getInstrumentation().getUiAutomation();
private boolean mRunTests;
+ private TestNetworkTracker mUpstreamTracker;
+
@Before
public void setUp() throws Exception {
// Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
@@ -138,16 +150,28 @@
}
private void cleanUp() throws Exception {
+ mTm.setPreferTestNetworks(false);
+
+ if (mUpstreamTracker != null) {
+ mUpstreamTracker.teardown();
+ mUpstreamTracker = null;
+ }
+ if (mUpstreamReader != null) {
+ TapPacketReader reader = mUpstreamReader;
+ mHandler.post(() -> reader.stop());
+ mUpstreamReader = null;
+ }
+
mTm.stopTethering(TETHERING_ETHERNET);
if (mTetheringEventCallback != null) {
mTetheringEventCallback.awaitInterfaceUntethered();
mTetheringEventCallback.unregister();
mTetheringEventCallback = null;
}
- if (mTapPacketReader != null) {
- TapPacketReader reader = mTapPacketReader;
+ if (mDownstreamReader != null) {
+ TapPacketReader reader = mDownstreamReader;
mHandler.post(() -> reader.stop());
- mTapPacketReader = null;
+ mDownstreamReader = null;
}
mHandlerThread.quitSafely();
mTetheredInterfaceRequester.release();
@@ -169,21 +193,21 @@
// This test requires manipulating packets. Skip if there is a physical Ethernet connected.
assumeFalse(mEm.isAvailable());
- mTestIface = createTestInterface();
+ mDownstreamIface = 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);
+ int mtu = getMTU(mDownstreamIface);
Log.d(TAG, "Including test interfaces");
mEm.setIncludeTestInterfaces(true);
final String iface = mTetheredInterfaceRequester.getInterface();
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), iface);
- checkVirtualEthernet(mTestIface, mtu);
+ checkVirtualEthernet(mDownstreamIface, mtu);
}
@Test
@@ -195,13 +219,13 @@
mEm.setIncludeTestInterfaces(true);
- mTestIface = createTestInterface();
+ mDownstreamIface = createTestInterface();
final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), iface);
- checkVirtualEthernet(mTestIface, getMTU(mTestIface));
+ checkVirtualEthernet(mDownstreamIface, getMTU(mDownstreamIface));
}
@Test
@@ -210,11 +234,11 @@
mEm.setIncludeTestInterfaces(true);
- mTestIface = createTestInterface();
+ mDownstreamIface = createTestInterface();
final String iface = mTetheredInterfaceRequester.getInterface();
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), iface);
assertInvalidStaticIpv4Request(iface, null, null);
assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
@@ -235,13 +259,14 @@
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);
+ FileDescriptor fd = mDownstreamIface.getFileDescriptor().getFileDescriptor();
+ mDownstreamReader = makePacketReader(fd, getMTU(mDownstreamIface));
+ TetheringTester tester = new TetheringTester(mDownstreamReader);
+ DhcpResults dhcpResults = tester.runDhcp(client1);
assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
try {
- runDhcp(fd, client2);
+ tester.runDhcp(client2);
fail("Only one client should get an IP address");
} catch (TimeoutException expected) { }
@@ -301,11 +326,11 @@
mEm.setIncludeTestInterfaces(true);
- mTestIface = createTestInterface();
+ mDownstreamIface = createTestInterface();
final String iface = mTetheredInterfaceRequester.getInterface();
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), iface);
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET)
.setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build();
@@ -316,10 +341,9 @@
// does not have an IP address, and unprivileged apps cannot see interfaces without IP
// addresses. This shouldn't be flaky because the TAP interface will buffer all packets even
// before the reader is started.
- FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor();
- mTapPacketReader = makePacketReader(fd, getMTU(mTestIface));
+ mDownstreamReader = makePacketReader(mDownstreamIface);
- expectRouterAdvertisement(mTapPacketReader, iface, 2000 /* timeoutMs */);
+ expectRouterAdvertisement(mDownstreamReader, iface, 2000 /* timeoutMs */);
expectLocalOnlyAddresses(iface);
}
@@ -354,12 +378,14 @@
private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1);
private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
+ private final CountDownLatch mUpstreamConnectedLatch = new CountDownLatch(1);
private final TetheringInterface mIface;
private volatile boolean mInterfaceWasTethered = false;
private volatile boolean mInterfaceWasLocalOnly = false;
private volatile boolean mUnregistered = false;
private volatile Collection<TetheredClient> mClients = null;
+ private volatile Network mUpstream = null;
MyTetheringEventCallback(TetheringManager tm, String iface) {
mTm = tm;
@@ -465,6 +491,22 @@
mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
return mClients;
}
+
+ @Override
+ public void onUpstreamChanged(Network network) {
+ // Ignore stale callbacks registered by previous test cases.
+ if (mUnregistered) return;
+
+ Log.d(TAG, "Got upstream changed: " + network);
+ mUpstream = network;
+ if (mUpstream != null) mUpstreamConnectedLatch.countDown();
+ }
+
+ public Network awaitFirstUpstreamConnected() throws Exception {
+ assertTrue("Did not receive upstream connected callback after " + TIMEOUT_MS + "ms",
+ mUpstreamConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ return mUpstream;
+ }
}
private MyTetheringEventCallback enableEthernetTethering(String iface,
@@ -508,6 +550,11 @@
return nif.getMTU();
}
+ private TapPacketReader makePacketReader(final TestNetworkInterface iface) throws Exception {
+ FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
+ return makePacketReader(fd, getMTU(iface));
+ }
+
private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
mHandler.post(() -> reader.start());
@@ -517,40 +564,18 @@
private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
- mTapPacketReader = makePacketReader(fd, mtu);
+ mDownstreamReader = makePacketReader(fd, mtu);
mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName());
- checkTetheredClientCallbacks(fd);
+ checkTetheredClientCallbacks(mDownstreamReader);
}
- 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 {
+ private void checkTetheredClientCallbacks(TapPacketReader packetReader) throws Exception {
// Create a fake client.
byte[] clientMacAddr = new byte[6];
new Random().nextBytes(clientMacAddr);
- DhcpResults dhcpResults = runDhcp(fd, clientMacAddr);
+ TetheringTester tester = new TetheringTester(packetReader);
+ DhcpResults dhcpResults = tester.runDhcp(clientMacAddr);
final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
assertEquals(1, clients.size());
@@ -563,7 +588,7 @@
// Check the hostname.
assertEquals(1, client.getAddresses().size());
TetheredClient.AddressInfo info = client.getAddresses().get(0);
- assertEquals(DHCP_HOSTNAME, info.getHostname());
+ assertEquals(TetheringTester.DHCP_HOSTNAME, info.getHostname());
// Check the address is the one that was handed out in the DHCP ACK.
assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
@@ -576,18 +601,6 @@
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 Handler mHandler;
private final EthernetManager mEm;
@@ -631,31 +644,6 @@
}
}
- 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);
@@ -701,10 +689,207 @@
}
private void maybeDeleteTestInterface() throws Exception {
- if (mTestIface != null) {
- mTestIface.getFileDescriptor().close();
- Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName());
- mTestIface = null;
+ if (mDownstreamIface != null) {
+ mDownstreamIface.getFileDescriptor().close();
+ Log.d(TAG, "Deleted test interface " + mDownstreamIface.getInterfaceName());
+ mDownstreamIface = null;
}
}
+
+ private TestNetworkTracker createTestUpstream(final List<LinkAddress> addresses)
+ throws Exception {
+ mTm.setPreferTestNetworks(true);
+
+ return initTestNetwork(mContext, addresses, TIMEOUT_MS);
+ }
+
+ @Test
+ public void testTestNetworkUpstream() throws Exception {
+ assumeFalse(mEm.isAvailable());
+
+ // MyTetheringEventCallback currently only support await first available upstream. Tethering
+ // may select internet network as upstream if test network is not available and not be
+ // preferred yet. Create test upstream network before enable tethering.
+ mUpstreamTracker = createTestUpstream(toList(TEST_IP4_ADDR, TEST_IP6_ADDR));
+
+ mDownstreamIface = createTestInterface();
+ mEm.setIncludeTestInterfaces(true);
+
+ final String iface = mTetheredInterfaceRequester.getInterface();
+ assertEquals("TetheredInterfaceCallback for unexpected interface",
+ mDownstreamIface.getInterfaceName(), iface);
+
+ mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName());
+ assertEquals("onUpstreamChanged for unexpected network", mUpstreamTracker.getNetwork(),
+ mTetheringEventCallback.awaitFirstUpstreamConnected());
+
+ mDownstreamReader = makePacketReader(mDownstreamIface);
+ // TODO: do basic forwarding test here.
+ }
+
+ // Test network topology:
+ //
+ // public network (rawip) private network
+ // | UE |
+ // +------------+ V +------------+------------+ V +------------+
+ // | Sever +---------+ Upstream | Downstream +---------+ Client |
+ // +------------+ +------------+------------+ +------------+
+ // remote ip public ip private ip
+ // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876
+ //
+ private static final Inet4Address REMOTE_IP4_ADDR =
+ (Inet4Address) parseNumericAddress("8.8.8.8");
+ // Used by public port and private port. Assume port 9876 has not been used yet before the
+ // testing that public port and private port are the same in the testing. Note that NAT port
+ // forwarding could be different between private port and public port.
+ private static final short LOCAL_PORT = 9876;
+ private static final short REMOTE_PORT = 433;
+ private static final byte TYPE_OF_SERVICE = 0;
+ private static final short ID = 27149;
+ private static final short ID2 = 27150;
+ private static final short FLAGS_AND_FRAGMENT_OFFSET = (short) 0x4000; // flags=DF, offset=0
+ private static final byte TIME_TO_LIVE = (byte) 0x40;
+ private static final ByteBuffer PAYLOAD =
+ ByteBuffer.wrap(new byte[] { (byte) 0x12, (byte) 0x34 });
+ private static final ByteBuffer PAYLOAD2 =
+ ByteBuffer.wrap(new byte[] { (byte) 0x56, (byte) 0x78 });
+
+ private boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEther,
+ @NonNull final ByteBuffer payload) {
+ final ByteBuffer buf = ByteBuffer.wrap(rawPacket);
+
+ if (hasEther) {
+ final EthernetHeader etherHeader = Struct.parse(EthernetHeader.class, buf);
+ if (etherHeader == null) return false;
+ }
+
+ final Ipv4Header ipv4Header = Struct.parse(Ipv4Header.class, buf);
+ if (ipv4Header == null) return false;
+
+ final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf);
+ if (udpHeader == null) return false;
+
+ if (buf.remaining() != payload.limit()) return false;
+
+ return Arrays.equals(Arrays.copyOfRange(buf.array(), buf.position(), buf.limit()),
+ payload.array());
+ }
+
+ @NonNull
+ private ByteBuffer buildUdpv4Packet(@Nullable final MacAddress srcMac,
+ @Nullable final MacAddress dstMac, short id,
+ @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp,
+ short srcPort, short dstPort, @Nullable final ByteBuffer payload)
+ throws Exception {
+ final boolean hasEther = (srcMac != null && dstMac != null);
+ final int payloadLen = (payload == null) ? 0 : payload.limit();
+ final ByteBuffer buffer = PacketBuilder.allocate(hasEther, IPPROTO_IP, IPPROTO_UDP,
+ payloadLen);
+ final PacketBuilder packetBuilder = new PacketBuilder(buffer);
+
+ if (hasEther) packetBuilder.writeL2Header(srcMac, dstMac, (short) ETHER_TYPE_IPV4);
+ packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
+ TIME_TO_LIVE, (byte) IPPROTO_UDP, srcIp, dstIp);
+ packetBuilder.writeUdpHeader(srcPort, dstPort);
+ if (payload != null) {
+ buffer.put(payload);
+ // in case data might be reused by caller, restore the position and
+ // limit of bytebuffer.
+ payload.clear();
+ }
+
+ return packetBuilder.finalizePacket();
+ }
+
+ @NonNull
+ private ByteBuffer buildUdpv4Packet(short id, @NonNull final Inet4Address srcIp,
+ @NonNull final Inet4Address dstIp, short srcPort, short dstPort,
+ @Nullable final ByteBuffer payload) throws Exception {
+ return buildUdpv4Packet(null /* srcMac */, null /* dstMac */, id, srcIp, dstIp, srcPort,
+ dstPort, payload);
+ }
+
+ // TODO: remove this verification once upstream connected notification race is fixed.
+ // See #runUdp4Test.
+ private boolean isIpv4TetherConnectivityVerified(TetheringTester tester,
+ RemoteResponder remote, TetheredDevice tethered) throws Exception {
+ final ByteBuffer probePacket = buildUdpv4Packet(tethered.macAddr,
+ tethered.routerMacAddr, ID, tethered.ipv4Addr /* srcIp */,
+ REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, REMOTE_PORT /*dstPort */,
+ TEST_REACHABILITY_PAYLOAD);
+
+ // Send a UDP packet from client and check the packet can be found on upstream interface.
+ for (int i = 0; i < TETHER_REACHABILITY_ATTEMPTS; i++) {
+ tester.sendPacket(probePacket);
+ byte[] expectedPacket = remote.getNextMatchedPacket(p -> {
+ Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+ return isExpectedUdpPacket(p, false /* hasEther */, TEST_REACHABILITY_PAYLOAD);
+ });
+ if (expectedPacket != null) return true;
+ }
+ return false;
+ }
+
+ private void runUdp4Test(TetheringTester tester, RemoteResponder remote) throws Exception {
+ final TetheredDevice tethered = tester.createTetheredDevice(MacAddress.fromString(
+ "1:2:3:4:5:6"));
+
+ // TODO: remove the connectivity verification for upstream connected notification race.
+ // Because async upstream connected notification can't guarantee the tethering routing is
+ // ready to use. Need to test tethering connectivity before testing.
+ // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes
+ // from upstream. That can guarantee that the routing is ready. Long term plan is that
+ // refactors upstream connected notification from async to sync.
+ assertTrue(isIpv4TetherConnectivityVerified(tester, remote, tethered));
+
+ // Send a UDP packet in original direction.
+ final ByteBuffer originalPacket = buildUdpv4Packet(tethered.macAddr,
+ tethered.routerMacAddr, ID, tethered.ipv4Addr /* srcIp */,
+ REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, REMOTE_PORT /*dstPort */,
+ PAYLOAD /* payload */);
+ tester.verifyUpload(remote, originalPacket, p -> {
+ Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+ return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD);
+ });
+
+ // Send a UDP packet in reply direction.
+ final Inet4Address publicIp4Addr = (Inet4Address) TEST_IP4_ADDR.getAddress();
+ final ByteBuffer replyPacket = buildUdpv4Packet(ID2, REMOTE_IP4_ADDR /* srcIp */,
+ publicIp4Addr /* dstIp */, REMOTE_PORT /* srcPort */, LOCAL_PORT /*dstPort */,
+ PAYLOAD2 /* payload */);
+ remote.verifyDownload(tester, replyPacket, p -> {
+ Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+ return isExpectedUdpPacket(p, true/* hasEther */, PAYLOAD2);
+ });
+ }
+
+ @Test
+ public void testUdpV4() throws Exception {
+ assumeFalse(mEm.isAvailable());
+
+ // MyTetheringEventCallback currently only support await first available upstream. Tethering
+ // may select internet network as upstream if test network is not available and not be
+ // preferred yet. Create test upstream network before enable tethering.
+ mUpstreamTracker = createTestUpstream(toList(TEST_IP4_ADDR));
+
+ mDownstreamIface = createTestInterface();
+ mEm.setIncludeTestInterfaces(true);
+
+ final String iface = mTetheredInterfaceRequester.getInterface();
+ assertEquals("TetheredInterfaceCallback for unexpected interface",
+ mDownstreamIface.getInterfaceName(), iface);
+
+ mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName());
+ assertEquals("onUpstreamChanged for unexpected network", mUpstreamTracker.getNetwork(),
+ mTetheringEventCallback.awaitFirstUpstreamConnected());
+
+ mDownstreamReader = makePacketReader(mDownstreamIface);
+ mUpstreamReader = makePacketReader(mUpstreamTracker.getTestIface());
+
+ runUdp4Test(new TetheringTester(mDownstreamReader), new RemoteResponder(mUpstreamReader));
+ }
+
+ private <T> List<T> toList(T... array) {
+ return Arrays.asList(array);
+ }
}
diff --git a/Tethering/tests/integration/src/android/net/TetheringTester.java b/Tethering/tests/integration/src/android/net/TetheringTester.java
new file mode 100644
index 0000000..d24661a
--- /dev/null
+++ b/Tethering/tests/integration/src/android/net/TetheringTester.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY;
+import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.net.dhcp.DhcpAckPacket;
+import android.net.dhcp.DhcpOfferPacket;
+import android.net.dhcp.DhcpPacket;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.networkstack.arp.ArpPacket;
+import com.android.testutils.TapPacketReader;
+
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Predicate;
+
+/**
+ * A class simulate tethered client. When caller create TetheringTester, it would connect to
+ * tethering module that do the dhcp and slaac to obtain ipv4 and ipv6 address. Then caller can
+ * send/receive packets by this class.
+ */
+public final class TetheringTester {
+ private static final String TAG = TetheringTester.class.getSimpleName();
+ 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,
+ };
+
+ public static final String DHCP_HOSTNAME = "testhostname";
+
+ private final ArrayMap<MacAddress, TetheredDevice> mTetheredDevices;
+ private final TapPacketReader mDownstreamReader;
+
+ public TetheringTester(TapPacketReader downstream) {
+ if (downstream == null) fail("Downstream reader could not be NULL");
+
+ mDownstreamReader = downstream;
+ mTetheredDevices = new ArrayMap<>();
+ }
+
+ public TetheredDevice createTetheredDevice(MacAddress macAddr) throws Exception {
+ if (mTetheredDevices.get(macAddr) != null) {
+ fail("Tethered device already created");
+ }
+
+ TetheredDevice tethered = new TetheredDevice(macAddr);
+ mTetheredDevices.put(macAddr, tethered);
+
+ return tethered;
+ }
+
+ public class TetheredDevice {
+ public final MacAddress macAddr;
+ public final MacAddress routerMacAddr;
+ public final Inet4Address ipv4Addr;
+
+ private TetheredDevice(MacAddress mac) throws Exception {
+ macAddr = mac;
+
+ DhcpResults dhcpResults = runDhcp(macAddr.toByteArray());
+ ipv4Addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
+ routerMacAddr = getRouterMacAddressFromArp(ipv4Addr, macAddr,
+ dhcpResults.serverAddress);
+ }
+ }
+
+ /** Simulate dhcp client to obtain ipv4 address. */
+ public DhcpResults runDhcp(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(clientMacAddr);
+ offerPacket = getNextDhcpPacket();
+ if (offerPacket instanceof DhcpOfferPacket) break;
+ }
+ if (!(offerPacket instanceof DhcpOfferPacket)) {
+ throw new TimeoutException("No DHCPOFFER received on interface within timeout");
+ }
+
+ sendDhcpRequest(offerPacket, clientMacAddr);
+ DhcpPacket ackPacket = getNextDhcpPacket();
+ if (!(ackPacket instanceof DhcpAckPacket)) {
+ throw new TimeoutException("No DHCPACK received on interface within timeout");
+ }
+
+ return ackPacket.toDhcpResults();
+ }
+
+ private void sendDhcpDiscover(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);
+ mDownstreamReader.sendResponse(packet);
+ }
+
+ private void sendDhcpRequest(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);
+ mDownstreamReader.sendResponse(packet);
+ }
+
+ private DhcpPacket getNextDhcpPacket() throws Exception {
+ final byte[] packet = getNextMatchedPacket((p) -> {
+ // Test whether this is DHCP packet.
+ try {
+ DhcpPacket.decodeFullPacket(p, p.length, DhcpPacket.ENCAP_L2);
+ } catch (DhcpPacket.ParseException e) {
+ // Not a DHCP packet.
+ return false;
+ }
+
+ return true;
+ });
+
+ return packet == null ? null :
+ DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2);
+ }
+
+ @Nullable
+ private ArpPacket parseArpPacket(final byte[] packet) {
+ try {
+ return ArpPacket.parseArpPacket(packet, packet.length);
+ } catch (ArpPacket.ParseException e) {
+ return null;
+ }
+ }
+
+ private void maybeReplyArp(byte[] packet) {
+ ByteBuffer buf = ByteBuffer.wrap(packet);
+
+ final ArpPacket arpPacket = parseArpPacket(packet);
+ if (arpPacket == null || arpPacket.opCode != ARP_REQUEST) return;
+
+ for (int i = 0; i < mTetheredDevices.size(); i++) {
+ TetheredDevice tethered = mTetheredDevices.valueAt(i);
+ if (!arpPacket.targetIp.equals(tethered.ipv4Addr)) continue;
+
+ final ByteBuffer arpReply = ArpPacket.buildArpPacket(
+ arpPacket.senderHwAddress.toByteArray() /* dst */,
+ tethered.macAddr.toByteArray() /* srcMac */,
+ arpPacket.senderIp.getAddress() /* target IP */,
+ arpPacket.senderHwAddress.toByteArray() /* target HW address */,
+ tethered.ipv4Addr.getAddress() /* sender IP */,
+ (short) ARP_REPLY);
+ try {
+ sendPacket(arpReply);
+ } catch (Exception e) {
+ fail("Failed to reply ARP for " + tethered.ipv4Addr);
+ }
+ return;
+ }
+ }
+
+ private MacAddress getRouterMacAddressFromArp(final Inet4Address tetherIp,
+ final MacAddress tetherMac, final Inet4Address routerIp) throws Exception {
+ final ByteBuffer arpProbe = ArpPacket.buildArpPacket(ETHER_BROADCAST /* dst */,
+ tetherMac.toByteArray() /* srcMac */, routerIp.getAddress() /* target IP */,
+ new byte[ETHER_ADDR_LEN] /* target HW address */,
+ tetherIp.getAddress() /* sender IP */, (short) ARP_REQUEST);
+ sendPacket(arpProbe);
+
+ final byte[] packet = getNextMatchedPacket((p) -> {
+ final ArpPacket arpPacket = parseArpPacket(p);
+ if (arpPacket == null || arpPacket.opCode != ARP_REPLY) return false;
+ return arpPacket.targetIp.equals(tetherIp);
+ });
+
+ if (packet != null) {
+ Log.d(TAG, "Get Mac address from ARP");
+ final ArpPacket arpReply = ArpPacket.parseArpPacket(packet, packet.length);
+ return arpReply.senderHwAddress;
+ }
+
+ fail("Could not get ARP packet");
+ return null;
+ }
+
+ public void sendPacket(ByteBuffer packet) throws Exception {
+ mDownstreamReader.sendResponse(packet);
+ }
+
+ public byte[] getNextMatchedPacket(Predicate<byte[]> filter) {
+ byte[] packet;
+ while ((packet = mDownstreamReader.poll(PACKET_READ_TIMEOUT_MS)) != null) {
+ if (filter.test(packet)) return packet;
+
+ maybeReplyArp(packet);
+ }
+
+ return null;
+ }
+
+ public void verifyUpload(final RemoteResponder dst, final ByteBuffer packet,
+ final Predicate<byte[]> filter) throws Exception {
+ sendPacket(packet);
+ assertNotNull("Upload fail", dst.getNextMatchedPacket(filter));
+ }
+
+ public static class RemoteResponder {
+ final TapPacketReader mUpstreamReader;
+ public RemoteResponder(TapPacketReader reader) {
+ mUpstreamReader = reader;
+ }
+
+ public void sendPacket(ByteBuffer packet) throws Exception {
+ mUpstreamReader.sendResponse(packet);
+ }
+
+ public byte[] getNextMatchedPacket(Predicate<byte[]> filter) throws Exception {
+ return mUpstreamReader.poll(PACKET_READ_TIMEOUT_MS, filter);
+ }
+
+ public void verifyDownload(final TetheringTester dst, final ByteBuffer packet,
+ final Predicate<byte[]> filter) throws Exception {
+ sendPacket(packet);
+ assertNotNull("Download fail", dst.getNextMatchedPacket(filter));
+ }
+ }
+}
diff --git a/Tethering/tests/mts/Android.bp b/Tethering/tests/mts/Android.bp
index 221771f..18fd63b 100644
--- a/Tethering/tests/mts/Android.bp
+++ b/Tethering/tests/mts/Android.bp
@@ -22,7 +22,7 @@
name: "MtsTetheringTestLatestSdk",
min_sdk_version: "30",
- target_sdk_version: "30",
+ target_sdk_version: "31",
libs: [
"android.test.base",
@@ -50,6 +50,8 @@
"libstaticjvmtiagent",
],
+ defaults: ["framework-connectivity-test-defaults"],
+
platform_apis: true,
// Tag this module as a mts test artifact
diff --git a/Tethering/tests/privileged/Android.bp b/Tethering/tests/privileged/Android.bp
index 75fdd6e..c890197 100644
--- a/Tethering/tests/privileged/Android.bp
+++ b/Tethering/tests/privileged/Android.bp
@@ -23,9 +23,10 @@
jni_libs: [
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
],
jni_uses_sdk_apis: true,
+ jarjar_rules: ":TetheringTestsJarJarRules",
visibility: ["//visibility:private"],
}
@@ -33,6 +34,7 @@
name: "TetheringPrivilegedTests",
defaults: [
"TetheringPrivilegedTestsJniDefaults",
+ "ConnectivityNextEnableDefaults",
],
srcs: [
"src/**/*.java",
@@ -42,7 +44,7 @@
platform_apis: true,
test_suites: [
"device-tests",
- "mts",
+ "mts-tethering",
],
static_libs: [
"androidx.test.rules",
diff --git a/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
index 23d9055..eb9cf71 100644
--- a/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
+++ b/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -20,6 +20,7 @@
import static com.android.net.module.util.IpUtils.icmpv6Checksum;
import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
+import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -30,7 +31,6 @@
import android.net.InetAddresses;
import android.net.MacAddress;
import android.net.util.InterfaceParams;
-import android.net.util.TetheringUtils;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@@ -40,6 +40,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.networkstack.tethering.util.TetheringUtils;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.TapPacketReader;
@@ -80,7 +81,7 @@
@BeforeClass
public static void setupOnce() {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
final IBinder netdIBinder =
diff --git a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
index 1d94214..34f3e0e 100644
--- a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
+++ b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
@@ -44,7 +44,6 @@
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.shared.RouteUtils;
import android.net.util.InterfaceParams;
import android.os.Handler;
import android.os.HandlerThread;
@@ -56,6 +55,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.net.module.util.Ipv6Utils;
+import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv6Header;
@@ -335,7 +335,7 @@
final String iface = mTetheredParams.name;
final RouteInfo linkLocalRoute =
new RouteInfo(new IpPrefix("fe80::/64"), null, iface, RTN_UNICAST);
- RouteUtils.addRoutesToLocalNetwork(sNetd, iface, List.of(linkLocalRoute));
+ NetdUtils.addRoutesToLocalNetwork(sNetd, iface, List.of(linkLocalRoute));
final ByteBuffer rs = createRsPacket("fe80::1122:3344:5566:7788");
mTetheredPacketReader.sendResponse(rs);
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
index f97270c..ad2faa0 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
@@ -18,6 +18,8 @@
import static android.system.OsConstants.ETH_P_IPV6;
+import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -31,6 +33,7 @@
import android.system.OsConstants;
import android.util.ArrayMap;
+import com.android.net.module.util.BpfMap;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -47,7 +50,7 @@
@RunWith(DevSdkIgnoreRunner.class)
@IgnoreUpTo(Build.VERSION_CODES.R)
public final class BpfMapTest {
- // Sync from packages/modules/Connectivity/Tethering/bpf_progs/offload.c.
+ // Sync from packages/modules/Connectivity/bpf_progs/offload.c.
private static final int TEST_MAP_SIZE = 16;
private static final String TETHER_DOWNSTREAM6_FS_PATH =
"/sys/fs/bpf/tethering/map_test_tether_downstream6_map";
@@ -58,7 +61,7 @@
@BeforeClass
public static void setupOnce() {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
}
@Before
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index f6e29cd..ecd1a39 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -33,6 +33,7 @@
sdk_version: "core_platform",
libs: [
"framework-minus-apex",
+ "framework-connectivity.impl",
"framework-tethering.impl",
],
visibility: [
@@ -48,7 +49,6 @@
"src/**/*.kt",
],
static_libs: [
- "TetheringApiCurrentLib",
"TetheringCommonTests",
"androidx.test.rules",
"frameworks-base-testutils",
@@ -66,6 +66,7 @@
"ext",
"framework-minus-apex",
"framework-res",
+ "framework-connectivity.impl",
"framework-tethering.impl",
"framework-wifi.stubs.module_lib",
],
@@ -73,7 +74,7 @@
// For mockito extended
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
],
}
@@ -83,7 +84,10 @@
android_library {
name: "TetheringTestsLatestSdkLib",
defaults: ["TetheringTestsDefaults"],
- target_sdk_version: "30",
+ static_libs: [
+ "TetheringApiStableLib",
+ ],
+ target_sdk_version: "31",
visibility: [
"//packages/modules/Connectivity/tests:__subpackages__",
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
@@ -95,9 +99,15 @@
platform_apis: true,
test_suites: [
"device-tests",
- "mts",
+ "mts-tethering",
],
- defaults: ["TetheringTestsDefaults"],
+ defaults: [
+ "TetheringTestsDefaults",
+ "ConnectivityNextEnableDefaults",
+ ],
+ static_libs: [
+ "TetheringApiCurrentLib",
+ ],
compile_multilib: "both",
jarjar_rules: ":TetheringTestsJarJarRules",
}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index ef4330a..267c376 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -16,6 +16,7 @@
package android.net.ip;
+import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
@@ -33,6 +34,7 @@
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import static android.system.OsConstants.ETH_P_IPV6;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_DELNEIGH;
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNEIGH;
@@ -85,8 +87,6 @@
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;
@@ -99,10 +99,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.BpfMap;
import com.android.net.module.util.NetworkStackConstants;
import com.android.networkstack.tethering.BpfCoordinator;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.BpfMap;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import com.android.networkstack.tethering.Tether4Key;
import com.android.networkstack.tethering.Tether4Value;
@@ -116,6 +116,8 @@
import com.android.networkstack.tethering.TetherStatsValue;
import com.android.networkstack.tethering.TetherUpstream6Key;
import com.android.networkstack.tethering.TetheringConfiguration;
+import com.android.networkstack.tethering.util.InterfaceSet;
+import com.android.networkstack.tethering.util.PrefixUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -400,11 +402,16 @@
}
@Test
- public void canBeTethered() throws Exception {
+ public void canBeTetheredAsBluetooth() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
- InOrder inOrder = inOrder(mCallback, mNetd);
+ InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
+ if (isAtLeastT()) {
+ 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);
// One for ipv4 route, one for ipv6 link local route.
@@ -426,7 +433,13 @@
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)));
+ // One is ipv4 address clear (set to 0.0.0.0), another is set interface down which only
+ // happen after T. Before T, the interface configuration control in bluetooth side.
+ if (isAtLeastT()) {
+ inOrder.verify(mNetd).interfaceSetCfg(
+ argThat(cfg -> assertContainsFlag(cfg.flags, IF_STATE_DOWN)));
+ }
+ inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> cfg.flags.length == 0));
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
@@ -443,7 +456,7 @@
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)));
+ 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),
@@ -587,7 +600,8 @@
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(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
+ argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
inOrder.verify(mBpfCoordinator).tetherOffloadClientClear(mIpServer);
inOrder.verify(mBpfCoordinator).stopMonitoring(mIpServer);
@@ -683,7 +697,11 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- assertDhcpStarted(mBluetoothPrefix);
+ if (isAtLeastT()) {
+ assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
+ } else {
+ assertDhcpStarted(mBluetoothPrefix);
+ }
}
@Test
@@ -1371,7 +1389,6 @@
for (String flag : flags) {
if (flag.equals(match)) return true;
}
- fail("Missing flag: " + match);
return false;
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index acc042b..c5969d2 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -97,6 +97,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.net.module.util.BpfMap;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.Struct;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
index 4865e03..3c07580 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.Intent;
-import android.net.ITetheringConnector;
import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
@@ -72,8 +71,8 @@
mBase = base;
}
- public ITetheringConnector getTetheringConnector() {
- return ITetheringConnector.Stub.asInterface(mBase);
+ public IBinder getIBinder() {
+ return mBase;
}
public MockTetheringService getService() {
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
index 24716b2..d1891ed 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -16,13 +16,13 @@
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 com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
+import static com.android.networkstack.tethering.util.TetheringUtils.uint16;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 41d46e5..6c98f2f 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -23,7 +23,8 @@
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 com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index 1b52f6e..f664d5d 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -26,6 +26,9 @@
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
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.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
@@ -40,10 +43,13 @@
import android.net.IIntResultListener;
import android.net.ITetheringConnector;
import android.net.ITetheringEventCallback;
+import android.net.TetheringManager;
import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
import android.os.Bundle;
+import android.os.ConditionVariable;
import android.os.Handler;
+import android.os.IBinder;
import android.os.ResultReceiver;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +68,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.function.Supplier;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class TetheringServiceTest {
@@ -113,7 +123,7 @@
InstrumentationRegistry.getTargetContext(),
MockTetheringService.class);
mMockConnector = (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent);
- mTetheringConnector = mMockConnector.getTetheringConnector();
+ mTetheringConnector = ITetheringConnector.Stub.asInterface(mMockConnector.getIBinder());
final MockTetheringService service = mMockConnector.getService();
mTethering = service.getTethering();
}
@@ -493,4 +503,81 @@
verifyNoMoreInteractionsForTethering();
});
}
+
+ private class ConnectorSupplier<T> implements Supplier<T> {
+ private T mResult = null;
+
+ public void set(T result) {
+ mResult = result;
+ }
+
+ @Override
+ public T get() {
+ return mResult;
+ }
+ }
+
+ private void forceGc() {
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ }
+
+ @Test
+ public void testTetheringManagerLeak() throws Exception {
+ runAsAccessNetworkState((none) -> {
+ final ArrayList<ITetheringEventCallback> callbacks = new ArrayList<>();
+ final ConditionVariable registeredCv = new ConditionVariable(false);
+ doAnswer((invocation) -> {
+ final Object[] args = invocation.getArguments();
+ callbacks.add((ITetheringEventCallback) args[0]);
+ registeredCv.open();
+ return null;
+ }).when(mTethering).registerTetheringEventCallback(any());
+
+ doAnswer((invocation) -> {
+ final Object[] args = invocation.getArguments();
+ callbacks.remove((ITetheringEventCallback) args[0]);
+ return null;
+ }).when(mTethering).unregisterTetheringEventCallback(any());
+
+ final ConnectorSupplier<IBinder> supplier = new ConnectorSupplier<>();
+
+ TetheringManager tm = new TetheringManager(mMockConnector.getService(), supplier);
+ assertNotNull(tm);
+ assertEquals("Internal callback should not be registered", 0, callbacks.size());
+
+ final WeakReference<TetheringManager> weakTm = new WeakReference(tm);
+ assertNotNull(weakTm.get());
+
+ // TetheringManager couldn't be GCed because pollingConnector thread implicitly
+ // reference TetheringManager object.
+ tm = null;
+ forceGc();
+ assertNotNull(weakTm.get());
+
+ // After getting connector, pollingConnector thread stops and internal callback is
+ // registered.
+ supplier.set(mMockConnector.getIBinder());
+ final long timeout = 500L;
+ if (!registeredCv.block(timeout)) {
+ fail("TetheringManager poll connector fail after " + timeout + " ms");
+ }
+ assertEquals("Internal callback is not registered", 1, callbacks.size());
+ assertNotNull(weakTm.get());
+
+ final int attempts = 100;
+ final long waitIntervalMs = 50;
+ for (int i = 0; i < attempts; i++) {
+ forceGc();
+ if (weakTm.get() == null) break;
+
+ Thread.sleep(waitIntervalMs);
+ }
+ assertNull("TetheringManager weak reference still not null after " + attempts
+ + " attempts", weakTm.get());
+
+ assertEquals("Internal callback is not unregistered", 0, callbacks.size());
+ });
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index f45768f..e4dbc7d 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -62,6 +62,7 @@
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
@@ -81,6 +82,8 @@
import static org.junit.Assert.assertFalse;
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 static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Matchers.anyInt;
@@ -182,6 +185,10 @@
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.net.module.util.CollectionUtils;
+import com.android.networkstack.apishim.common.BluetoothPanShim;
+import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
+import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
import com.android.testutils.MiscAsserts;
@@ -261,6 +268,8 @@
@Mock private PackageManager mPackageManager;
@Mock private BluetoothAdapter mBluetoothAdapter;
@Mock private BluetoothPan mBluetoothPan;
+ @Mock private BluetoothPanShim mBluetoothPanShim;
+ @Mock private TetheredInterfaceRequestShim mTetheredInterfaceRequestShim;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -285,6 +294,7 @@
private PrivateAddressCoordinator mPrivateAddressCoordinator;
private SoftApCallback mSoftApCallback;
private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
+ private TetheredInterfaceCallbackShim mTetheredInterfaceCallbackShim;
private TestConnectivityManager mCm;
@@ -483,13 +493,23 @@
return false;
}
-
@Override
public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
TetheringConfiguration cfg) {
mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg);
return mPrivateAddressCoordinator;
}
+
+ @Override
+ public BluetoothPanShim getBluetoothPanShim(BluetoothPan pan) {
+ try {
+ when(mBluetoothPanShim.requestTetheredInterface(
+ any(), any())).thenReturn(mTetheredInterfaceRequestShim);
+ } catch (UnsupportedApiLevelException e) {
+ fail("BluetoothPan#requestTetheredInterface is not supported");
+ }
+ return mBluetoothPanShim;
+ }
}
private static LinkProperties buildUpstreamLinkProperties(String interfaceName,
@@ -2557,11 +2577,49 @@
@Test
public void testBluetoothTethering() throws Exception {
+ // Switch to @IgnoreUpTo(Build.VERSION_CODES.S_V2) when it is available for AOSP.
+ assumeTrue(isAtLeastT());
+
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
- when(mBluetoothAdapter.isEnabled()).thenReturn(true);
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
mLooper.dispatchAll();
- verifySetBluetoothTethering(true);
+ verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
+ result.assertHasResult();
+
+ mTetheredInterfaceCallbackShim.onAvailable(TEST_BT_IFNAME);
+ mLooper.dispatchAll();
+ verifyNetdCommandForBtSetup();
+
+ // If PAN disconnect, tethering should also be stopped.
+ mTetheredInterfaceCallbackShim.onUnavailable();
+ mLooper.dispatchAll();
+ verifyNetdCommandForBtTearDown();
+
+ // Tethering could restart if PAN reconnect.
+ mTetheredInterfaceCallbackShim.onAvailable(TEST_BT_IFNAME);
+ mLooper.dispatchAll();
+ verifyNetdCommandForBtSetup();
+
+ // Pretend that bluetooth tethering was disabled.
+ mockBluetoothSettings(true /* bluetoothOn */, false /* tetheringOn */);
+ mTethering.stopTethering(TETHERING_BLUETOOTH);
+ mLooper.dispatchAll();
+ verifySetBluetoothTethering(false /* enable */, false /* bindToPanService */);
+
+ verifyNetdCommandForBtTearDown();
+ }
+
+ @Test
+ public void testBluetoothTetheringBeforeT() throws Exception {
+ // Switch to @IgnoreAfter(Build.VERSION_CODES.S_V2) when it is available for AOSP.
+ assumeFalse(isAtLeastT());
+
+ final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
+ mLooper.dispatchAll();
+ verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
result.assertHasResult();
mTethering.interfaceAdded(TEST_BT_IFNAME);
@@ -2574,6 +2632,73 @@
mLooper.dispatchAll();
tetherResult.assertHasResult();
+ verifyNetdCommandForBtSetup();
+
+ // Turning tethering on a second time does not bind to the PAN service again, since it's
+ // already bound.
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+ final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), secondResult);
+ mLooper.dispatchAll();
+ verifySetBluetoothTethering(true /* enable */, false /* bindToPanService */);
+ secondResult.assertHasResult();
+
+ mockBluetoothSettings(true /* bluetoothOn */, false /* tetheringOn */);
+ mTethering.stopTethering(TETHERING_BLUETOOTH);
+ mLooper.dispatchAll();
+ final ResultListener untetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mTethering.untether(TEST_BT_IFNAME, untetherResult);
+ mLooper.dispatchAll();
+ untetherResult.assertHasResult();
+ verifySetBluetoothTethering(false /* enable */, false /* bindToPanService */);
+
+ verifyNetdCommandForBtTearDown();
+ }
+
+ @Test
+ public void testBluetoothServiceDisconnects() throws Exception {
+ final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
+ mLooper.dispatchAll();
+ ServiceListener panListener = verifySetBluetoothTethering(true /* enable */,
+ true /* bindToPanService */);
+ result.assertHasResult();
+
+ mTethering.interfaceAdded(TEST_BT_IFNAME);
+ mLooper.dispatchAll();
+
+ if (isAtLeastT()) {
+ mTetheredInterfaceCallbackShim.onAvailable(TEST_BT_IFNAME);
+ mLooper.dispatchAll();
+ } else {
+ mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
+ mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true);
+ final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
+ mLooper.dispatchAll();
+ tetherResult.assertHasResult();
+ }
+
+ verifyNetdCommandForBtSetup();
+
+ panListener.onServiceDisconnected(BluetoothProfile.PAN);
+ mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
+ mLooper.dispatchAll();
+
+ verifyNetdCommandForBtTearDown();
+ }
+
+ private void mockBluetoothSettings(boolean bluetoothOn, boolean tetheringOn) {
+ when(mBluetoothAdapter.isEnabled()).thenReturn(bluetoothOn);
+ when(mBluetoothPan.isTetheringOn()).thenReturn(tetheringOn);
+ }
+
+ private void verifyNetdCommandForBtSetup() throws Exception {
+ if (isAtLeastT()) {
+ verify(mNetd).interfaceSetCfg(argThat(cfg -> TEST_BT_IFNAME.equals(cfg.ifName)
+ && assertContainsFlag(cfg.flags, INetd.IF_STATE_UP)));
+ }
verify(mNetd).tetherInterfaceAdd(TEST_BT_IFNAME);
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
@@ -2584,39 +2709,64 @@
anyString(), anyString());
verifyNoMoreInteractions(mNetd);
reset(mNetd);
+ }
- when(mBluetoothAdapter.isEnabled()).thenReturn(true);
- mTethering.stopTethering(TETHERING_BLUETOOTH);
- mLooper.dispatchAll();
- final ResultListener untetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- mTethering.untether(TEST_BT_IFNAME, untetherResult);
- mLooper.dispatchAll();
- untetherResult.assertHasResult();
- verifySetBluetoothTethering(false);
+ private boolean assertContainsFlag(String[] flags, String match) {
+ for (String flag : flags) {
+ if (flag.equals(match)) return true;
+ }
+ return false;
+ }
+ private void verifyNetdCommandForBtTearDown() throws Exception {
verify(mNetd).tetherApplyDnsInterfaces();
verify(mNetd).tetherInterfaceRemove(TEST_BT_IFNAME);
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
- verify(mNetd).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
+ // One is ipv4 address clear (set to 0.0.0.0), another is set interface down which only
+ // happen after T. Before T, the interface configuration control in bluetooth side.
+ verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
+ any(InterfaceConfigurationParcel.class));
verify(mNetd).tetherStop();
verify(mNetd).ipfwdDisableForwarding(TETHERING_NAME);
- verifyNoMoreInteractions(mNetd);
+ reset(mNetd);
}
- private void verifySetBluetoothTethering(final boolean enable) {
- final ArgumentCaptor<ServiceListener> listenerCaptor =
- ArgumentCaptor.forClass(ServiceListener.class);
+ // If bindToPanService is true, this function would return ServiceListener which could notify
+ // PanService is connected or disconnected.
+ private ServiceListener verifySetBluetoothTethering(final boolean enable,
+ final boolean bindToPanService) throws Exception {
+ ServiceListener listener = null;
verify(mBluetoothAdapter).isEnabled();
- verify(mBluetoothAdapter).getProfileProxy(eq(mServiceContext), listenerCaptor.capture(),
- eq(BluetoothProfile.PAN));
- final ServiceListener listener = listenerCaptor.getValue();
- when(mBluetoothPan.isTetheringOn()).thenReturn(enable);
- listener.onServiceConnected(BluetoothProfile.PAN, mBluetoothPan);
- verify(mBluetoothPan).setBluetoothTethering(enable);
+ if (bindToPanService) {
+ final ArgumentCaptor<ServiceListener> listenerCaptor =
+ ArgumentCaptor.forClass(ServiceListener.class);
+ verify(mBluetoothAdapter).getProfileProxy(eq(mServiceContext), listenerCaptor.capture(),
+ eq(BluetoothProfile.PAN));
+ listener = listenerCaptor.getValue();
+ listener.onServiceConnected(BluetoothProfile.PAN, mBluetoothPan);
+ mLooper.dispatchAll();
+ } else {
+ verify(mBluetoothAdapter, never()).getProfileProxy(eq(mServiceContext), any(),
+ anyInt());
+ }
+
+ if (isAtLeastT()) {
+ if (enable) {
+ final ArgumentCaptor<TetheredInterfaceCallbackShim> callbackCaptor =
+ ArgumentCaptor.forClass(TetheredInterfaceCallbackShim.class);
+ verify(mBluetoothPanShim).requestTetheredInterface(any(), callbackCaptor.capture());
+ mTetheredInterfaceCallbackShim = callbackCaptor.getValue();
+ } else {
+ verify(mTetheredInterfaceRequestShim).release();
+ }
+ } else {
+ verify(mBluetoothPan).setBluetoothTethering(enable);
+ }
verify(mBluetoothPan).isTetheringOn();
- verify(mBluetoothAdapter).closeProfileProxy(eq(BluetoothProfile.PAN), eq(mBluetoothPan));
verifyNoMoreInteractions(mBluetoothAdapter, mBluetoothPan);
reset(mBluetoothAdapter, mBluetoothPan);
+
+ return listener;
}
private void runDualStackUsbTethering(final String expectedIface) throws Exception {
diff --git a/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/InterfaceSetTest.java
similarity index 97%
rename from Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
rename to Tethering/tests/unit/src/com/android/networkstack/tethering/util/InterfaceSetTest.java
index ea084b6..d52dc0f 100644
--- a/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/InterfaceSetTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
diff --git a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
similarity index 99%
rename from Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
rename to Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
index e5d0b1c..94ce2b6 100644
--- a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_USB;
diff --git a/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/VersionedBroadcastListenerTest.java
similarity index 98%
rename from Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
rename to Tethering/tests/unit/src/com/android/networkstack/tethering/util/VersionedBroadcastListenerTest.java
index 5a9b6e3..b7dc66e 100644
--- a/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/VersionedBroadcastListenerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.reset;
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
new file mode 100644
index 0000000..bb9f5ead6
--- /dev/null
+++ b/bpf_progs/Android.bp
@@ -0,0 +1,99 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// struct definitions shared with JNI
+//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_headers {
+ name: "bpf_connectivity_headers",
+ vendor_available: false,
+ host_supported: false,
+ header_libs: ["bpf_headers"],
+ export_header_lib_headers: ["bpf_headers"],
+ export_include_dirs: ["."],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ sdk_version: "30",
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.tethering",
+ ],
+ visibility: [
+ // TODO: remove it when NetworkStatsService is moved into the mainline module and no more
+ // calls to JNI in libservices.core.
+ "//frameworks/base/services/core/jni",
+ "//packages/modules/Connectivity/Tethering",
+ "//packages/modules/Connectivity/tests/unit/jni",
+ // TODO: remove system/netd/* when all BPF code is moved out of Netd.
+ "//system/netd/libnetdbpf",
+ "//system/netd/server",
+ "//system/netd/tests",
+ ],
+}
+
+//
+// bpf kernel programs
+//
+bpf {
+ name: "offload.o",
+ srcs: ["offload.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+bpf {
+ name: "test.o",
+ srcs: ["test.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+bpf {
+ name: "clatd.o_mainline",
+ srcs: ["clatd.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ include_dirs: [
+ "frameworks/libs/net/common/netd/libnetdutils/include",
+ ],
+ sub_dir: "net_shared",
+}
+
+bpf {
+ name: "netd.o_mainline",
+ srcs: ["netd.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ include_dirs: [
+ "frameworks/libs/net/common/netd/libnetdutils/include",
+ ],
+ sub_dir: "net_shared",
+}
diff --git a/Tethering/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
similarity index 100%
rename from Tethering/bpf_progs/bpf_net_helpers.h
rename to bpf_progs/bpf_net_helpers.h
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
new file mode 100644
index 0000000..8577d9d
--- /dev/null
+++ b/bpf_progs/bpf_shared.h
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <netdutils/UidConstants.h>
+
+// This header file is shared by eBPF kernel programs (C) and netd (C++) and
+// some of the maps are also accessed directly from Java mainline module code.
+//
+// Hence: explicitly pad all relevant structures and assert that their size
+// is the sum of the sizes of their fields.
+#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
+
+typedef struct {
+ uint32_t uid;
+ uint32_t tag;
+} UidTagValue;
+STRUCT_SIZE(UidTagValue, 2 * 4); // 8
+
+typedef struct {
+ uint32_t uid;
+ uint32_t tag;
+ uint32_t counterSet;
+ uint32_t ifaceIndex;
+} StatsKey;
+STRUCT_SIZE(StatsKey, 4 * 4); // 16
+
+typedef struct {
+ uint64_t rxPackets;
+ uint64_t rxBytes;
+ uint64_t txPackets;
+ uint64_t txBytes;
+} StatsValue;
+STRUCT_SIZE(StatsValue, 4 * 8); // 32
+
+typedef struct {
+ char name[IFNAMSIZ];
+} IfaceValue;
+STRUCT_SIZE(IfaceValue, 16);
+
+typedef struct {
+ uint64_t rxBytes;
+ uint64_t rxPackets;
+ uint64_t txBytes;
+ uint64_t txPackets;
+ uint64_t tcpRxPackets;
+ uint64_t tcpTxPackets;
+} Stats;
+
+// Since we cannot garbage collect the stats map since device boot, we need to make these maps as
+// large as possible. The maximum size of number of map entries we can have is depend on the rlimit
+// of MEM_LOCK granted to netd. The memory space needed by each map can be calculated by the
+// following fomula:
+// elem_size = 40 + roundup(key_size, 8) + roundup(value_size, 8)
+// cost = roundup_pow_of_two(max_entries) * 16 + elem_size * max_entries +
+// elem_size * number_of_CPU
+// And the cost of each map currently used is(assume the device have 8 CPUs):
+// cookie_tag_map: key: 8 bytes, value: 8 bytes, cost: 822592 bytes = 823Kbytes
+// uid_counter_set_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// app_uid_stats_map: key: 4 bytes, value: 32 bytes, cost: 1062784 bytes = 1063Kbytes
+// uid_stats_map: key: 16 bytes, value: 32 bytes, cost: 1142848 bytes = 1143Kbytes
+// tag_stats_map: key: 16 bytes, value: 32 bytes, cost: 1142848 bytes = 1143Kbytes
+// iface_index_name_map:key: 4 bytes, value: 16 bytes, cost: 80896 bytes = 81Kbytes
+// iface_stats_map: key: 4 bytes, value: 32 bytes, cost: 97024 bytes = 97Kbytes
+// dozable_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// standby_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// powersave_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// total: 4930Kbytes
+// It takes maximum 4.9MB kernel memory space if all maps are full, which requires any devices
+// running this module to have a memlock rlimit to be larger then 5MB. In the old qtaguid module,
+// we don't have a total limit for data entries but only have limitation of tags each uid can have.
+// (default is 1024 in kernel);
+
+// 'static' - otherwise these constants end up in .rodata in the resulting .o post compilation
+static const int COOKIE_UID_MAP_SIZE = 10000;
+static const int UID_COUNTERSET_MAP_SIZE = 2000;
+static const int APP_STATS_MAP_SIZE = 10000;
+static const int STATS_MAP_SIZE = 5000;
+static const int IFACE_INDEX_NAME_MAP_SIZE = 1000;
+static const int IFACE_STATS_MAP_SIZE = 1000;
+static const int CONFIGURATION_MAP_SIZE = 2;
+static const int UID_OWNER_MAP_SIZE = 2000;
+
+#define BPF_PATH "/sys/fs/bpf/"
+
+#define BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_egress_stats"
+#define BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_ingress_stats"
+#define XT_BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_skfilter_ingress_xtbpf"
+#define XT_BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_skfilter_egress_xtbpf"
+#define XT_BPF_ALLOWLIST_PROG_PATH BPF_PATH "prog_netd_skfilter_allowlist_xtbpf"
+#define XT_BPF_DENYLIST_PROG_PATH BPF_PATH "prog_netd_skfilter_denylist_xtbpf"
+#define CGROUP_SOCKET_PROG_PATH BPF_PATH "prog_netd_cgroupsock_inet_create"
+
+#define TC_BPF_INGRESS_ACCOUNT_PROG_NAME "prog_netd_schedact_ingress_account"
+#define TC_BPF_INGRESS_ACCOUNT_PROG_PATH BPF_PATH TC_BPF_INGRESS_ACCOUNT_PROG_NAME
+
+#define COOKIE_TAG_MAP_PATH BPF_PATH "map_netd_cookie_tag_map"
+#define UID_COUNTERSET_MAP_PATH BPF_PATH "map_netd_uid_counterset_map"
+#define APP_UID_STATS_MAP_PATH BPF_PATH "map_netd_app_uid_stats_map"
+#define STATS_MAP_A_PATH BPF_PATH "map_netd_stats_map_A"
+#define STATS_MAP_B_PATH BPF_PATH "map_netd_stats_map_B"
+#define IFACE_INDEX_NAME_MAP_PATH BPF_PATH "map_netd_iface_index_name_map"
+#define IFACE_STATS_MAP_PATH BPF_PATH "map_netd_iface_stats_map"
+#define CONFIGURATION_MAP_PATH BPF_PATH "map_netd_configuration_map"
+#define UID_OWNER_MAP_PATH BPF_PATH "map_netd_uid_owner_map"
+#define UID_PERMISSION_MAP_PATH BPF_PATH "map_netd_uid_permission_map"
+
+enum UidOwnerMatchType {
+ NO_MATCH = 0,
+ HAPPY_BOX_MATCH = (1 << 0),
+ PENALTY_BOX_MATCH = (1 << 1),
+ DOZABLE_MATCH = (1 << 2),
+ STANDBY_MATCH = (1 << 3),
+ POWERSAVE_MATCH = (1 << 4),
+ RESTRICTED_MATCH = (1 << 5),
+ IIF_MATCH = (1 << 6),
+};
+
+enum BpfPermissionMatch {
+ BPF_PERMISSION_INTERNET = 1 << 2,
+ BPF_PERMISSION_UPDATE_DEVICE_STATS = 1 << 3,
+};
+// In production we use two identical stats maps to record per uid stats and
+// do swap and clean based on the configuration specified here. The statsMapType
+// value in configuration map specified which map is currently in use.
+enum StatsMapType {
+ SELECT_MAP_A,
+ SELECT_MAP_B,
+};
+
+// TODO: change the configuration object from an 8-bit bitmask to an object with clearer
+// semantics, like a struct.
+typedef uint8_t BpfConfig;
+static const BpfConfig DEFAULT_CONFIG = 0;
+
+typedef struct {
+ // Allowed interface index. Only applicable if IIF_MATCH is set in the rule bitmask above.
+ uint32_t iif;
+ // A bitmask of enum values in UidOwnerMatchType.
+ uint32_t rule;
+} UidOwnerValue;
+STRUCT_SIZE(UidOwnerValue, 2 * 4); // 8
+
+#define UID_RULES_CONFIGURATION_KEY 1
+#define CURRENT_STATS_MAP_CONFIGURATION_KEY 2
+
+#define CLAT_INGRESS6_PROG_RAWIP_NAME "prog_clatd_schedcls_ingress6_clat_rawip"
+#define CLAT_INGRESS6_PROG_ETHER_NAME "prog_clatd_schedcls_ingress6_clat_ether"
+
+#define CLAT_INGRESS6_PROG_RAWIP_PATH BPF_PATH CLAT_INGRESS6_PROG_RAWIP_NAME
+#define CLAT_INGRESS6_PROG_ETHER_PATH BPF_PATH CLAT_INGRESS6_PROG_ETHER_NAME
+
+#define CLAT_INGRESS6_MAP_PATH BPF_PATH "map_clatd_clat_ingress6_map"
+
+typedef struct {
+ uint32_t iif; // The input interface index
+ struct in6_addr pfx96; // The source /96 nat64 prefix, bottom 32 bits must be 0
+ struct in6_addr local6; // The full 128-bits of the destination IPv6 address
+} ClatIngress6Key;
+STRUCT_SIZE(ClatIngress6Key, 4 + 2 * 16); // 36
+
+typedef struct {
+ uint32_t oif; // The output interface to redirect to (0 means don't redirect)
+ struct in_addr local4; // The destination IPv4 address
+} ClatIngress6Value;
+STRUCT_SIZE(ClatIngress6Value, 4 + 4); // 8
+
+#define CLAT_EGRESS4_PROG_RAWIP_NAME "prog_clatd_schedcls_egress4_clat_rawip"
+#define CLAT_EGRESS4_PROG_ETHER_NAME "prog_clatd_schedcls_egress4_clat_ether"
+
+#define CLAT_EGRESS4_PROG_RAWIP_PATH BPF_PATH CLAT_EGRESS4_PROG_RAWIP_NAME
+#define CLAT_EGRESS4_PROG_ETHER_PATH BPF_PATH CLAT_EGRESS4_PROG_ETHER_NAME
+
+#define CLAT_EGRESS4_MAP_PATH BPF_PATH "map_clatd_clat_egress4_map"
+
+typedef struct {
+ uint32_t iif; // The input interface index
+ struct in_addr local4; // The source IPv4 address
+} ClatEgress4Key;
+STRUCT_SIZE(ClatEgress4Key, 4 + 4); // 8
+
+typedef struct {
+ uint32_t oif; // The output interface to redirect to
+ struct in6_addr local6; // The full 128-bits of the source IPv6 address
+ struct in6_addr pfx96; // The destination /96 nat64 prefix, bottom 32 bits must be 0
+ bool oifIsEthernet; // Whether the output interface requires ethernet header
+ uint8_t pad[3];
+} ClatEgress4Value;
+STRUCT_SIZE(ClatEgress4Value, 4 + 2 * 16 + 1 + 3); // 40
+
+#undef STRUCT_SIZE
diff --git a/Tethering/bpf_progs/bpf_tethering.h b/bpf_progs/bpf_tethering.h
similarity index 98%
rename from Tethering/bpf_progs/bpf_tethering.h
rename to bpf_progs/bpf_tethering.h
index 5fdf8cd..b0ec8f6 100644
--- a/Tethering/bpf_progs/bpf_tethering.h
+++ b/bpf_progs/bpf_tethering.h
@@ -24,7 +24,7 @@
// Common definitions for BPF code in the tethering mainline module.
// These definitions are available to:
// - The BPF programs in Tethering/bpf_progs/
-// - JNI code that depends on the bpf_tethering_headers library.
+// - JNI code that depends on the bpf_connectivity_headers library.
#define BPF_TETHER_ERRORS \
ERR(INVALID_IP_VERSION) \
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
new file mode 100644
index 0000000..dc646c3
--- /dev/null
+++ b/bpf_progs/clatd.c
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+
+#include <linux/bpf.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/pkt_cls.h>
+#include <linux/swab.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+// bionic kernel uapi linux/udp.h header is munged...
+#define __kernel_udphdr udphdr
+#include <linux/udp.h>
+
+#include "bpf_helpers.h"
+#include "bpf_net_helpers.h"
+#include "bpf_shared.h"
+
+// From kernel:include/net/ip.h
+#define IP_DF 0x4000 // Flag: "Don't Fragment"
+
+DEFINE_BPF_MAP_GRW(clat_ingress6_map, HASH, ClatIngress6Key, ClatIngress6Value, 16, AID_SYSTEM)
+
+static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) {
+ const 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;
+ const struct ethhdr* const eth = is_ethernet ? data : NULL; // used iff is_ethernet
+ const struct ipv6hdr* const ip6 = is_ethernet ? (void*)(eth + 1) : data;
+
+ // Require ethernet dst mac address to be our unicast address.
+ if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_PIPE;
+
+ // Must be meta-ethernet IPv6 frame
+ if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_PIPE;
+
+ // Must have (ethernet and) ipv6 header
+ if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_PIPE;
+
+ // Ethertype - if present - must be IPv6
+ if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_PIPE;
+
+ // IP version must be 6
+ if (ip6->version != 6) return TC_ACT_PIPE;
+
+ // Maximum IPv6 payload length that can be translated to IPv4
+ if (ntohs(ip6->payload_len) > 0xFFFF - sizeof(struct iphdr)) return TC_ACT_PIPE;
+
+ switch (ip6->nexthdr) {
+ case IPPROTO_TCP: // For TCP & UDP the checksum neutrality of the chosen IPv6
+ case IPPROTO_UDP: // address means there is no need to update their checksums.
+ case IPPROTO_GRE: // We do not need to bother looking at GRE/ESP headers,
+ case IPPROTO_ESP: // since there is never a checksum to update.
+ break;
+
+ default: // do not know how to handle anything else
+ return TC_ACT_PIPE;
+ }
+
+ ClatIngress6Key k = {
+ .iif = skb->ifindex,
+ .pfx96.in6_u.u6_addr32 =
+ {
+ ip6->saddr.in6_u.u6_addr32[0],
+ ip6->saddr.in6_u.u6_addr32[1],
+ ip6->saddr.in6_u.u6_addr32[2],
+ },
+ .local6 = ip6->daddr,
+ };
+
+ ClatIngress6Value* v = bpf_clat_ingress6_map_lookup_elem(&k);
+
+ if (!v) return TC_ACT_PIPE;
+
+ struct ethhdr eth2; // used iff is_ethernet
+ if (is_ethernet) {
+ eth2 = *eth; // Copy over the ethernet header (src/dst mac)
+ eth2.h_proto = htons(ETH_P_IP); // But replace the ethertype
+ }
+
+ struct iphdr ip = {
+ .version = 4, // u4
+ .ihl = sizeof(struct iphdr) / sizeof(__u32), // u4
+ .tos = (ip6->priority << 4) + (ip6->flow_lbl[0] >> 4), // u8
+ .tot_len = htons(ntohs(ip6->payload_len) + sizeof(struct iphdr)), // u16
+ .id = 0, // u16
+ .frag_off = htons(IP_DF), // u16
+ .ttl = ip6->hop_limit, // u8
+ .protocol = ip6->nexthdr, // u8
+ .check = 0, // u16
+ .saddr = ip6->saddr.in6_u.u6_addr32[3], // u32
+ .daddr = v->local4.s_addr, // u32
+ };
+
+ // Calculate the IPv4 one's complement checksum of the IPv4 header.
+ __wsum sum4 = 0;
+ for (int i = 0; i < sizeof(ip) / sizeof(__u16); ++i) {
+ sum4 += ((__u16*)&ip)[i];
+ }
+ // Note that sum4 is guaranteed to be non-zero by virtue of ip.version == 4
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse u32 into range 1 .. 0x1FFFE
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse any potential carry into u16
+ ip.check = (__u16)~sum4; // sum4 cannot be zero, so this is never 0xFFFF
+
+ // Calculate the *negative* IPv6 16-bit one's complement checksum of the IPv6 header.
+ __wsum sum6 = 0;
+ // We'll end up with a non-zero sum due to ip6->version == 6 (which has '0' bits)
+ for (int i = 0; i < sizeof(*ip6) / sizeof(__u16); ++i) {
+ sum6 += ~((__u16*)ip6)[i]; // note the bitwise negation
+ }
+
+ // Note that there is no L4 checksum update: we are relying on the checksum neutrality
+ // of the ipv6 address chosen by netd's ClatdController.
+
+ // Packet mutations begin - point of no return, but if this first modification fails
+ // the packet is probably still pristine, so let clatd handle it.
+ if (bpf_skb_change_proto(skb, htons(ETH_P_IP), 0)) return TC_ACT_PIPE;
+
+ // This takes care of updating the skb->csum field for a CHECKSUM_COMPLETE packet.
+ //
+ // In such a case, skb->csum is a 16-bit one's complement sum of the entire payload,
+ // thus we need to subtract out the ipv6 header's sum, and add in the ipv4 header's sum.
+ // However, by construction of ip.check above the checksum of an ipv4 header is zero.
+ // Thus we only need to subtract the ipv6 header's sum, which is the same as adding
+ // in the sum of the bitwise negation of the ipv6 header.
+ //
+ // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
+ // (-ENOTSUPP) if it isn't. So we just ignore the return code.
+ //
+ // if (skb->ip_summed == CHECKSUM_COMPLETE)
+ // return (skb->csum = csum_add(skb->csum, csum));
+ // else
+ // return -ENOTSUPP;
+ bpf_csum_update(skb, sum6);
+
+ // bpf_skb_change_proto() invalidates all pointers - reload them.
+ data = (void*)(long)skb->data;
+ data_end = (void*)(long)skb->data_end;
+
+ // I cannot think of any valid way for this error condition to trigger, however I do
+ // believe the explicit check is required to keep the in kernel ebpf verifier happy.
+ if (data + l2_header_size + sizeof(struct iphdr) > data_end) return TC_ACT_SHOT;
+
+ if (is_ethernet) {
+ struct ethhdr* new_eth = data;
+
+ // Copy over the updated ethernet header
+ *new_eth = eth2;
+
+ // Copy over the new ipv4 header.
+ *(struct iphdr*)(new_eth + 1) = ip;
+ } else {
+ // Copy over the new ipv4 header without an ethernet header.
+ *(struct iphdr*)data = ip;
+ }
+
+ // Redirect, possibly back to same interface, so tcpdump sees packet twice.
+ if (v->oif) return bpf_redirect(v->oif, BPF_F_INGRESS);
+
+ // Just let it through, tcpdump will not see IPv4 packet.
+ return TC_ACT_PIPE;
+}
+
+DEFINE_BPF_PROG("schedcls/ingress6/clat_ether", AID_ROOT, AID_SYSTEM, sched_cls_ingress6_clat_ether)
+(struct __sk_buff* skb) {
+ return nat64(skb, true);
+}
+
+DEFINE_BPF_PROG("schedcls/ingress6/clat_rawip", AID_ROOT, AID_SYSTEM, sched_cls_ingress6_clat_rawip)
+(struct __sk_buff* skb) {
+ return nat64(skb, false);
+}
+
+DEFINE_BPF_MAP_GRW(clat_egress4_map, HASH, ClatEgress4Key, ClatEgress4Value, 16, AID_SYSTEM)
+
+DEFINE_BPF_PROG("schedcls/egress4/clat_ether", AID_ROOT, AID_SYSTEM, sched_cls_egress4_clat_ether)
+(struct __sk_buff* skb) {
+ return TC_ACT_PIPE;
+}
+
+DEFINE_BPF_PROG("schedcls/egress4/clat_rawip", AID_ROOT, AID_SYSTEM, sched_cls_egress4_clat_rawip)
+(struct __sk_buff* skb) {
+ void* data = (void*)(long)skb->data;
+ const void* data_end = (void*)(long)skb->data_end;
+ const struct iphdr* const ip4 = data;
+
+ // Must be meta-ethernet IPv4 frame
+ if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_PIPE;
+
+ // Must have ipv4 header
+ if (data + sizeof(*ip4) > data_end) return TC_ACT_PIPE;
+
+ // IP version must be 4
+ if (ip4->version != 4) return TC_ACT_PIPE;
+
+ // We cannot handle IP options, just standard 20 byte == 5 dword minimal IPv4 header
+ if (ip4->ihl != 5) return TC_ACT_PIPE;
+
+ // Calculate the IPv4 one's complement checksum of the IPv4 header.
+ __wsum sum4 = 0;
+ for (int i = 0; i < sizeof(*ip4) / sizeof(__u16); ++i) {
+ sum4 += ((__u16*)ip4)[i];
+ }
+ // Note that sum4 is guaranteed to be non-zero by virtue of ip4->version == 4
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse u32 into range 1 .. 0x1FFFE
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse any potential carry into u16
+ // for a correct checksum we should get *a* zero, but sum4 must be positive, ie 0xFFFF
+ if (sum4 != 0xFFFF) return TC_ACT_PIPE;
+
+ // Minimum IPv4 total length is the size of the header
+ if (ntohs(ip4->tot_len) < sizeof(*ip4)) return TC_ACT_PIPE;
+
+ // We are incapable of dealing with IPv4 fragments
+ if (ip4->frag_off & ~htons(IP_DF)) return TC_ACT_PIPE;
+
+ switch (ip4->protocol) {
+ case IPPROTO_TCP: // For TCP & UDP the checksum neutrality of the chosen IPv6
+ case IPPROTO_GRE: // address means there is no need to update their checksums.
+ case IPPROTO_ESP: // We do not need to bother looking at GRE/ESP headers,
+ break; // since there is never a checksum to update.
+
+ case IPPROTO_UDP: // See above comment, but must also have UDP header...
+ if (data + sizeof(*ip4) + sizeof(struct udphdr) > data_end) return TC_ACT_PIPE;
+ const struct udphdr* uh = (const struct udphdr*)(ip4 + 1);
+ // If IPv4/UDP checksum is 0 then fallback to clatd so it can calculate the
+ // checksum. Otherwise the network or more likely the NAT64 gateway might
+ // drop the packet because in most cases IPv6/UDP packets with a zero checksum
+ // are invalid. See RFC 6935. TODO: calculate checksum via bpf_csum_diff()
+ if (!uh->check) return TC_ACT_PIPE;
+ break;
+
+ default: // do not know how to handle anything else
+ return TC_ACT_PIPE;
+ }
+
+ ClatEgress4Key k = {
+ .iif = skb->ifindex,
+ .local4.s_addr = ip4->saddr,
+ };
+
+ ClatEgress4Value* v = bpf_clat_egress4_map_lookup_elem(&k);
+
+ if (!v) return TC_ACT_PIPE;
+
+ // Translating without redirecting doesn't make sense.
+ if (!v->oif) return TC_ACT_PIPE;
+
+ // This implementation is currently limited to rawip.
+ if (v->oifIsEthernet) return TC_ACT_PIPE;
+
+ struct ipv6hdr ip6 = {
+ .version = 6, // __u8:4
+ .priority = ip4->tos >> 4, // __u8:4
+ .flow_lbl = {(ip4->tos & 0xF) << 4, 0, 0}, // __u8[3]
+ .payload_len = htons(ntohs(ip4->tot_len) - 20), // __be16
+ .nexthdr = ip4->protocol, // __u8
+ .hop_limit = ip4->ttl, // __u8
+ .saddr = v->local6, // struct in6_addr
+ .daddr = v->pfx96, // struct in6_addr
+ };
+ ip6.daddr.in6_u.u6_addr32[3] = ip4->daddr;
+
+ // Calculate the IPv6 16-bit one's complement checksum of the IPv6 header.
+ __wsum sum6 = 0;
+ // We'll end up with a non-zero sum due to ip6.version == 6
+ for (int i = 0; i < sizeof(ip6) / sizeof(__u16); ++i) {
+ sum6 += ((__u16*)&ip6)[i];
+ }
+
+ // Note that there is no L4 checksum update: we are relying on the checksum neutrality
+ // of the ipv6 address chosen by netd's ClatdController.
+
+ // Packet mutations begin - point of no return, but if this first modification fails
+ // the packet is probably still pristine, so let clatd handle it.
+ if (bpf_skb_change_proto(skb, htons(ETH_P_IPV6), 0)) return TC_ACT_PIPE;
+
+ // This takes care of updating the skb->csum field for a CHECKSUM_COMPLETE packet.
+ //
+ // In such a case, skb->csum is a 16-bit one's complement sum of the entire payload,
+ // thus we need to subtract out the ipv4 header's sum, and add in the ipv6 header's sum.
+ // However, we've already verified the ipv4 checksum is correct and thus 0.
+ // Thus we only need to add the ipv6 header's sum.
+ //
+ // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
+ // (-ENOTSUPP) if it isn't. So we just ignore the return code (see above for more details).
+ bpf_csum_update(skb, sum6);
+
+ // bpf_skb_change_proto() invalidates all pointers - reload them.
+ data = (void*)(long)skb->data;
+ data_end = (void*)(long)skb->data_end;
+
+ // I cannot think of any valid way for this error condition to trigger, however I do
+ // believe the explicit check is required to keep the in kernel ebpf verifier happy.
+ if (data + sizeof(ip6) > data_end) return TC_ACT_SHOT;
+
+ // Copy over the new ipv6 header without an ethernet header.
+ *(struct ipv6hdr*)data = ip6;
+
+ // Redirect to non v4-* interface. Tcpdump only sees packet after this redirect.
+ return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
+}
+
+LICENSE("Apache 2.0");
+CRITICAL("netd");
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
new file mode 100644
index 0000000..72ee431
--- /dev/null
+++ b/bpf_progs/netd.c
@@ -0,0 +1,392 @@
+/*
+ * 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.
+ */
+
+#include <bpf_helpers.h>
+#include <linux/bpf.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/pkt_cls.h>
+#include <linux/tcp.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "bpf_net_helpers.h"
+#include "bpf_shared.h"
+
+// This is defined for cgroup bpf filter only.
+#define BPF_DROP_UNLESS_DNS 2
+#define BPF_PASS 1
+#define BPF_DROP 0
+
+// This is used for xt_bpf program only.
+#define BPF_NOMATCH 0
+#define BPF_MATCH 1
+
+#define BPF_EGRESS 0
+#define BPF_INGRESS 1
+
+#define IP_PROTO_OFF offsetof(struct iphdr, protocol)
+#define IPV6_PROTO_OFF offsetof(struct ipv6hdr, nexthdr)
+#define IPPROTO_IHL_OFF 0
+#define TCP_FLAG_OFF 13
+#define RST_OFFSET 2
+
+DEFINE_BPF_MAP_GRW(cookie_tag_map, HASH, uint64_t, UidTagValue, COOKIE_UID_MAP_SIZE,
+ AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRW(uid_counterset_map, HASH, uint32_t, uint8_t, UID_COUNTERSET_MAP_SIZE,
+ AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRW(app_uid_stats_map, HASH, uint32_t, StatsValue, APP_STATS_MAP_SIZE,
+ AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRW(stats_map_A, HASH, StatsKey, StatsValue, STATS_MAP_SIZE, AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRW(stats_map_B, HASH, StatsKey, StatsValue, STATS_MAP_SIZE, AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRW(iface_stats_map, HASH, uint32_t, StatsValue, IFACE_STATS_MAP_SIZE,
+ AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRW(configuration_map, HASH, uint32_t, uint8_t, CONFIGURATION_MAP_SIZE,
+ AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRW(uid_owner_map, HASH, uint32_t, UidOwnerValue, UID_OWNER_MAP_SIZE,
+ AID_NET_BW_ACCT)
+DEFINE_BPF_MAP_GRW(uid_permission_map, HASH, uint32_t, uint8_t, UID_OWNER_MAP_SIZE, AID_NET_BW_ACCT)
+
+/* never actually used from ebpf */
+DEFINE_BPF_MAP_GRW(iface_index_name_map, HASH, uint32_t, IfaceValue, IFACE_INDEX_NAME_MAP_SIZE,
+ AID_NET_BW_ACCT)
+
+static __always_inline int is_system_uid(uint32_t uid) {
+ return (uid <= MAX_SYSTEM_UID) && (uid >= MIN_SYSTEM_UID);
+}
+
+/*
+ * Note: this blindly assumes an MTU of 1500, and that packets > MTU are always TCP,
+ * and that TCP is using the Linux default settings with TCP timestamp option enabled
+ * which uses 12 TCP option bytes per frame.
+ *
+ * These are not unreasonable assumptions:
+ *
+ * The internet does not really support MTUs greater than 1500, so most TCP traffic will
+ * be at that MTU, or slightly below it (worst case our upwards adjustment is too small).
+ *
+ * The chance our traffic isn't IP at all is basically zero, so the IP overhead correction
+ * is bound to be needed.
+ *
+ * Furthermore, the likelyhood that we're having to deal with GSO (ie. > MTU) packets that
+ * are not IP/TCP is pretty small (few other things are supported by Linux) and worse case
+ * our extra overhead will be slightly off, but probably still better than assuming none.
+ *
+ * Most servers are also Linux and thus support/default to using TCP timestamp option
+ * (and indeed TCP timestamp option comes from RFC 1323 titled "TCP Extensions for High
+ * Performance" which also defined TCP window scaling and are thus absolutely ancient...).
+ *
+ * All together this should be more correct than if we simply ignored GSO frames
+ * (ie. counted them as single packets with no extra overhead)
+ *
+ * Especially since the number of packets is important for any future clat offload correction.
+ * (which adjusts upward by 20 bytes per packet to account for ipv4 -> ipv6 header conversion)
+ */
+#define DEFINE_UPDATE_STATS(the_stats_map, TypeOfKey) \
+ static __always_inline inline void update_##the_stats_map(struct __sk_buff* skb, \
+ int direction, TypeOfKey* key) { \
+ StatsValue* value = bpf_##the_stats_map##_lookup_elem(key); \
+ if (!value) { \
+ StatsValue newValue = {}; \
+ bpf_##the_stats_map##_update_elem(key, &newValue, BPF_NOEXIST); \
+ value = bpf_##the_stats_map##_lookup_elem(key); \
+ } \
+ if (value) { \
+ const int mtu = 1500; \
+ uint64_t packets = 1; \
+ uint64_t bytes = skb->len; \
+ if (bytes > mtu) { \
+ bool is_ipv6 = (skb->protocol == htons(ETH_P_IPV6)); \
+ int ip_overhead = (is_ipv6 ? sizeof(struct ipv6hdr) : sizeof(struct iphdr)); \
+ int tcp_overhead = ip_overhead + sizeof(struct tcphdr) + 12; \
+ int mss = mtu - tcp_overhead; \
+ uint64_t payload = bytes - tcp_overhead; \
+ packets = (payload + mss - 1) / mss; \
+ bytes = tcp_overhead * packets + payload; \
+ } \
+ if (direction == BPF_EGRESS) { \
+ __sync_fetch_and_add(&value->txPackets, packets); \
+ __sync_fetch_and_add(&value->txBytes, bytes); \
+ } else if (direction == BPF_INGRESS) { \
+ __sync_fetch_and_add(&value->rxPackets, packets); \
+ __sync_fetch_and_add(&value->rxBytes, bytes); \
+ } \
+ } \
+ }
+
+DEFINE_UPDATE_STATS(app_uid_stats_map, uint32_t)
+DEFINE_UPDATE_STATS(iface_stats_map, uint32_t)
+DEFINE_UPDATE_STATS(stats_map_A, StatsKey)
+DEFINE_UPDATE_STATS(stats_map_B, StatsKey)
+
+static inline bool skip_owner_match(struct __sk_buff* skb) {
+ int offset = -1;
+ int ret = 0;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ offset = IP_PROTO_OFF;
+ uint8_t proto, ihl;
+ uint8_t flag;
+ ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
+ if (!ret) {
+ if (proto == IPPROTO_ESP) {
+ return true;
+ } else if (proto == IPPROTO_TCP) {
+ ret = bpf_skb_load_bytes(skb, IPPROTO_IHL_OFF, &ihl, 1);
+ ihl = ihl & 0x0F;
+ ret = bpf_skb_load_bytes(skb, ihl * 4 + TCP_FLAG_OFF, &flag, 1);
+ if (ret == 0 && (flag >> RST_OFFSET & 1)) {
+ return true;
+ }
+ }
+ }
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ offset = IPV6_PROTO_OFF;
+ uint8_t proto;
+ ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
+ if (!ret) {
+ if (proto == IPPROTO_ESP) {
+ return true;
+ } else if (proto == IPPROTO_TCP) {
+ uint8_t flag;
+ ret = bpf_skb_load_bytes(skb, sizeof(struct ipv6hdr) + TCP_FLAG_OFF, &flag, 1);
+ if (ret == 0 && (flag >> RST_OFFSET & 1)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static __always_inline BpfConfig getConfig(uint32_t configKey) {
+ uint32_t mapSettingKey = configKey;
+ BpfConfig* config = bpf_configuration_map_lookup_elem(&mapSettingKey);
+ if (!config) {
+ // Couldn't read configuration entry. Assume everything is disabled.
+ return DEFAULT_CONFIG;
+ }
+ return *config;
+}
+
+static inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid, int direction) {
+ if (skip_owner_match(skb)) return BPF_PASS;
+
+ if (is_system_uid(uid)) return BPF_PASS;
+
+ BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
+
+ UidOwnerValue* uidEntry = bpf_uid_owner_map_lookup_elem(&uid);
+ uint8_t uidRules = uidEntry ? uidEntry->rule : 0;
+ uint32_t allowed_iif = uidEntry ? uidEntry->iif : 0;
+
+ if (enabledRules) {
+ if ((enabledRules & DOZABLE_MATCH) && !(uidRules & DOZABLE_MATCH)) {
+ return BPF_DROP;
+ }
+ if ((enabledRules & STANDBY_MATCH) && (uidRules & STANDBY_MATCH)) {
+ return BPF_DROP;
+ }
+ if ((enabledRules & POWERSAVE_MATCH) && !(uidRules & POWERSAVE_MATCH)) {
+ return BPF_DROP;
+ }
+ if ((enabledRules & RESTRICTED_MATCH) && !(uidRules & RESTRICTED_MATCH)) {
+ return BPF_DROP;
+ }
+ }
+ if (direction == BPF_INGRESS && (uidRules & IIF_MATCH)) {
+ // Drops packets not coming from lo nor the allowlisted interface
+ if (allowed_iif && skb->ifindex != 1 && skb->ifindex != allowed_iif) {
+ return BPF_DROP_UNLESS_DNS;
+ }
+ }
+ return BPF_PASS;
+}
+
+static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, int direction,
+ StatsKey* key, uint8_t selectedMap) {
+ if (selectedMap == SELECT_MAP_A) {
+ update_stats_map_A(skb, direction, key);
+ } else if (selectedMap == SELECT_MAP_B) {
+ update_stats_map_B(skb, direction, key);
+ }
+}
+
+static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
+ uint32_t sock_uid = bpf_get_socket_uid(skb);
+ uint64_t cookie = bpf_get_socket_cookie(skb);
+ UidTagValue* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
+ uint32_t uid, tag;
+ if (utag) {
+ uid = utag->uid;
+ tag = utag->tag;
+ } else {
+ uid = sock_uid;
+ tag = 0;
+ }
+
+ // Always allow and never count clat traffic. Only the IPv4 traffic on the stacked
+ // interface is accounted for and subject to usage restrictions.
+ // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
+ if (sock_uid == AID_CLAT || uid == AID_CLAT) {
+ return BPF_PASS;
+ }
+
+ int match = bpf_owner_match(skb, sock_uid, direction);
+ if ((direction == BPF_EGRESS) && (match == BPF_DROP)) {
+ // If an outbound packet is going to be dropped, we do not count that
+ // traffic.
+ return match;
+ }
+
+// Workaround for secureVPN with VpnIsolation enabled, refer to b/159994981 for details.
+// Keep TAG_SYSTEM_DNS in sync with DnsResolver/include/netd_resolv/resolv.h
+// and TrafficStatsConstants.java
+#define TAG_SYSTEM_DNS 0xFFFFFF82
+ if (tag == TAG_SYSTEM_DNS && uid == AID_DNS) {
+ uid = sock_uid;
+ if (match == BPF_DROP_UNLESS_DNS) match = BPF_PASS;
+ } else {
+ if (match == BPF_DROP_UNLESS_DNS) match = BPF_DROP;
+ }
+
+ StatsKey key = {.uid = uid, .tag = tag, .counterSet = 0, .ifaceIndex = skb->ifindex};
+
+ uint8_t* counterSet = bpf_uid_counterset_map_lookup_elem(&uid);
+ if (counterSet) key.counterSet = (uint32_t)*counterSet;
+
+ uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
+ uint8_t* selectedMap = bpf_configuration_map_lookup_elem(&mapSettingKey);
+
+ // Use asm("%0 &= 1" : "+r"(match)) before return match,
+ // to help kernel's bpf verifier, so that it can be 100% certain
+ // that the returned value is always BPF_NOMATCH(0) or BPF_MATCH(1).
+ if (!selectedMap) {
+ asm("%0 &= 1" : "+r"(match));
+ return match;
+ }
+
+ if (key.tag) {
+ update_stats_with_config(skb, direction, &key, *selectedMap);
+ key.tag = 0;
+ }
+
+ update_stats_with_config(skb, direction, &key, *selectedMap);
+ update_app_uid_stats_map(skb, direction, &uid);
+ asm("%0 &= 1" : "+r"(match));
+ return match;
+}
+
+DEFINE_BPF_PROG("cgroupskb/ingress/stats", AID_ROOT, AID_ROOT, bpf_cgroup_ingress)
+(struct __sk_buff* skb) {
+ return bpf_traffic_account(skb, BPF_INGRESS);
+}
+
+DEFINE_BPF_PROG("cgroupskb/egress/stats", AID_ROOT, AID_ROOT, bpf_cgroup_egress)
+(struct __sk_buff* skb) {
+ return bpf_traffic_account(skb, BPF_EGRESS);
+}
+
+DEFINE_BPF_PROG("skfilter/egress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_egress_prog)
+(struct __sk_buff* skb) {
+ // Clat daemon does not generate new traffic, all its traffic is accounted for already
+ // on the v4-* interfaces (except for the 20 (or 28) extra bytes of IPv6 vs IPv4 overhead,
+ // but that can be corrected for later when merging v4-foo stats into interface foo's).
+ // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
+ uint32_t sock_uid = bpf_get_socket_uid(skb);
+ if (sock_uid == AID_CLAT) return BPF_NOMATCH;
+ if (sock_uid == AID_SYSTEM) {
+ uint64_t cookie = bpf_get_socket_cookie(skb);
+ UidTagValue* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
+ if (utag && utag->uid == AID_CLAT) return BPF_NOMATCH;
+ }
+
+ uint32_t key = skb->ifindex;
+ update_iface_stats_map(skb, BPF_EGRESS, &key);
+ return BPF_MATCH;
+}
+
+DEFINE_BPF_PROG("skfilter/ingress/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_ingress_prog)
+(struct __sk_buff* skb) {
+ // Clat daemon traffic is not accounted by virtue of iptables raw prerouting drop rule
+ // (in clat_raw_PREROUTING chain), which triggers before this (in bw_raw_PREROUTING chain).
+ // It will be accounted for on the v4-* clat interface instead.
+ // Keep that in mind when moving this out of iptables xt_bpf and into tc ingress (or xdp).
+
+ uint32_t key = skb->ifindex;
+ update_iface_stats_map(skb, BPF_INGRESS, &key);
+ return BPF_MATCH;
+}
+
+DEFINE_BPF_PROG("schedact/ingress/account", AID_ROOT, AID_NET_ADMIN, tc_bpf_ingress_account_prog)
+(struct __sk_buff* skb) {
+ // Account for ingress traffic before tc drops it.
+ uint32_t key = skb->ifindex;
+ update_iface_stats_map(skb, BPF_INGRESS, &key);
+ return TC_ACT_UNSPEC;
+}
+
+DEFINE_BPF_PROG("skfilter/allowlist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_allowlist_prog)
+(struct __sk_buff* skb) {
+ uint32_t sock_uid = bpf_get_socket_uid(skb);
+ if (is_system_uid(sock_uid)) return BPF_MATCH;
+
+ // 65534 is the overflow 'nobody' uid, usually this being returned means
+ // that skb->sk is NULL during RX (early decap socket lookup failure),
+ // which commonly happens for incoming packets to an unconnected udp socket.
+ // Additionally bpf_get_socket_cookie() returns 0 if skb->sk is NULL
+ if ((sock_uid == 65534) && !bpf_get_socket_cookie(skb) && is_received_skb(skb))
+ return BPF_MATCH;
+
+ UidOwnerValue* allowlistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
+ if (allowlistMatch) return allowlistMatch->rule & HAPPY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH;
+ return BPF_NOMATCH;
+}
+
+DEFINE_BPF_PROG("skfilter/denylist/xtbpf", AID_ROOT, AID_NET_ADMIN, xt_bpf_denylist_prog)
+(struct __sk_buff* skb) {
+ uint32_t sock_uid = bpf_get_socket_uid(skb);
+ UidOwnerValue* denylistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
+ if (denylistMatch) return denylistMatch->rule & PENALTY_BOX_MATCH ? BPF_MATCH : BPF_NOMATCH;
+ return BPF_NOMATCH;
+}
+
+DEFINE_BPF_PROG_KVER("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create,
+ KVER(4, 14, 0))
+(struct bpf_sock* sk) {
+ uint64_t gid_uid = bpf_get_current_uid_gid();
+ /*
+ * A given app is guaranteed to have the same app ID in all the profiles in
+ * which it is installed, and install permission is granted to app for all
+ * user at install time so we only check the appId part of a request uid at
+ * run time. See UserHandle#isSameApp for detail.
+ */
+ uint32_t appId = (gid_uid & 0xffffffff) % PER_USER_RANGE;
+ uint8_t* permissions = bpf_uid_permission_map_lookup_elem(&appId);
+ if (!permissions) {
+ // UID not in map. Default to just INTERNET permission.
+ return 1;
+ }
+
+ // A return value of 1 means allow, everything else means deny.
+ return (*permissions & BPF_PERMISSION_INTERNET) == BPF_PERMISSION_INTERNET;
+}
+
+LICENSE("Apache 2.0");
+CRITICAL("netd");
diff --git a/Tethering/bpf_progs/offload.c b/bpf_progs/offload.c
similarity index 99%
rename from Tethering/bpf_progs/offload.c
rename to bpf_progs/offload.c
index 51fed76..977e918 100644
--- a/Tethering/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -24,6 +24,9 @@
#define __kernel_udphdr udphdr
#include <linux/udp.h>
+// The resulting .o needs to load on the Android S bpfloader v0.2
+#define BPFLOADER_MIN_VER 2u
+
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
#include "bpf_tethering.h"
diff --git a/Tethering/bpf_progs/test.c b/bpf_progs/test.c
similarity index 93%
rename from Tethering/bpf_progs/test.c
rename to bpf_progs/test.c
index 3f0df2e..a76e346 100644
--- a/Tethering/bpf_progs/test.c
+++ b/bpf_progs/test.c
@@ -18,6 +18,9 @@
#include <linux/in.h>
#include <linux/ip.h>
+// The resulting .o needs to load on the Android S bpfloader v0.2
+#define BPFLOADER_MIN_VER 2u
+
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
#include "bpf_tethering.h"
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
new file mode 100644
index 0000000..abcfbeb
--- /dev/null
+++ b/framework-t/Android.bp
@@ -0,0 +1,71 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_defaults {
+ name: "enable-framework-connectivity-t-targets",
+ enabled: true,
+}
+// The above defaults can be used to disable framework-connectivity t
+// targets while minimizing merge conflicts in the build rules.
+
+
+java_sdk_library {
+ name: "framework-connectivity-tiramisu",
+ sdk_version: "module_current",
+ min_sdk_version: "Tiramisu",
+ defaults: [
+ "framework-module-defaults",
+ "enable-framework-connectivity-t-targets",
+ ],
+ srcs: [
+ ":framework-connectivity-tiramisu-updatable-sources",
+ ],
+ libs: [
+ "unsupportedappusage",
+ "app-compat-annotations",
+ ],
+ permitted_packages: [
+ "android.net",
+ "android.net.nsd",
+ ],
+ apex_available: [
+ "com.android.tethering",
+ ],
+ impl_library_visibility: [
+ "//packages/modules/Connectivity/Tethering/apex",
+ // In preparation for future move
+ "//packages/modules/Connectivity/apex",
+ "//packages/modules/Connectivity/service-t",
+ "//frameworks/base",
+
+ // Tests using hidden APIs
+ "//cts/tests/netlegacy22.api",
+ "//external/sl4a:__subpackages__",
+ "//frameworks/libs/net/common/testutils",
+ "//frameworks/libs/net/common/tests:__subpackages__",
+ "//frameworks/opt/telephony/tests/telephonytests",
+ "//packages/modules/CaptivePortalLogin/tests",
+ "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
+ "//packages/modules/Connectivity/tests:__subpackages__",
+ "//packages/modules/NetworkStack/tests:__subpackages__",
+ "//packages/modules/Wifi/service/tests/wifitests",
+ ],
+}
diff --git a/framework-t/api/current.txt b/framework-t/api/current.txt
new file mode 100644
index 0000000..0443456
--- /dev/null
+++ b/framework-t/api/current.txt
@@ -0,0 +1,60 @@
+// Signature format: 2.0
+package android.net.nsd {
+
+ public final class NsdManager {
+ method public void discoverServices(String, int, android.net.nsd.NsdManager.DiscoveryListener);
+ method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener);
+ method public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener);
+ method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener);
+ method public void unregisterService(android.net.nsd.NsdManager.RegistrationListener);
+ field public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED";
+ field public static final String EXTRA_NSD_STATE = "nsd_state";
+ field public static final int FAILURE_ALREADY_ACTIVE = 3; // 0x3
+ field public static final int FAILURE_INTERNAL_ERROR = 0; // 0x0
+ field public static final int FAILURE_MAX_LIMIT = 4; // 0x4
+ field public static final int NSD_STATE_DISABLED = 1; // 0x1
+ field public static final int NSD_STATE_ENABLED = 2; // 0x2
+ field public static final int PROTOCOL_DNS_SD = 1; // 0x1
+ }
+
+ public static interface NsdManager.DiscoveryListener {
+ method public void onDiscoveryStarted(String);
+ method public void onDiscoveryStopped(String);
+ method public void onServiceFound(android.net.nsd.NsdServiceInfo);
+ method public void onServiceLost(android.net.nsd.NsdServiceInfo);
+ method public void onStartDiscoveryFailed(String, int);
+ method public void onStopDiscoveryFailed(String, int);
+ }
+
+ public static interface NsdManager.RegistrationListener {
+ method public void onRegistrationFailed(android.net.nsd.NsdServiceInfo, int);
+ method public void onServiceRegistered(android.net.nsd.NsdServiceInfo);
+ method public void onServiceUnregistered(android.net.nsd.NsdServiceInfo);
+ method public void onUnregistrationFailed(android.net.nsd.NsdServiceInfo, int);
+ }
+
+ public static interface NsdManager.ResolveListener {
+ method public void onResolveFailed(android.net.nsd.NsdServiceInfo, int);
+ method public void onServiceResolved(android.net.nsd.NsdServiceInfo);
+ }
+
+ public final class NsdServiceInfo implements android.os.Parcelable {
+ ctor public NsdServiceInfo();
+ method public int describeContents();
+ method public java.util.Map<java.lang.String,byte[]> getAttributes();
+ method public java.net.InetAddress getHost();
+ method public int getPort();
+ method public String getServiceName();
+ method public String getServiceType();
+ method public void removeAttribute(String);
+ method public void setAttribute(String, String);
+ method public void setHost(java.net.InetAddress);
+ method public void setPort(int);
+ method public void setServiceName(String);
+ method public void setServiceType(String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.nsd.NsdServiceInfo> CREATOR;
+ }
+
+}
+
diff --git a/framework-t/api/module-lib-current.txt b/framework-t/api/module-lib-current.txt
new file mode 100644
index 0000000..81d89c6
--- /dev/null
+++ b/framework-t/api/module-lib-current.txt
@@ -0,0 +1,9 @@
+// Signature format: 2.0
+package android.net {
+
+ public final class ConnectivityFrameworkInitializerTiramisu {
+ method public static void registerServiceWrappers();
+ }
+
+}
+
diff --git a/framework-t/api/module-lib-removed.txt b/framework-t/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/framework-t/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/framework-t/api/removed.txt b/framework-t/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/framework-t/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/framework-t/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/framework-t/api/system-removed.txt b/framework-t/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/framework-t/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/framework/Android.bp b/framework/Android.bp
index ee3a5d5..de505c7 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -19,27 +19,6 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-java_library {
- name: "framework-connectivity-protos",
- sdk_version: "module_current",
- min_sdk_version: "30",
- proto: {
- type: "nano",
- },
- srcs: [
- // TODO: consider moving relevant .proto files directly to the module directory
- ":framework-javastream-protos",
- ],
- apex_available: [
- "//apex_available:platform",
- "com.android.tethering",
- ],
- jarjar_rules: "jarjar-rules-proto.txt",
- visibility: [
- "//visibility:private",
- ],
-}
-
filegroup {
name: "framework-connectivity-internal-sources",
srcs: [
@@ -87,6 +66,7 @@
":net-utils-framework-common-srcs",
],
aidl: {
+ generate_get_transaction_name: true,
include_dirs: [
// Include directories for parcelables that are part of the stable API, and need a
// one-line "parcelable X" .aidl declaration to be used in AIDL interfaces.
@@ -101,12 +81,12 @@
"framework-wifi.stubs.module_lib",
"net-utils-device-common",
],
+ static_libs: [
+ "modules-utils-build",
+ ],
libs: [
"unsupportedappusage",
],
- static_libs: [
- "framework-connectivity-protos",
- ],
jarjar_rules: "jarjar-rules.txt",
permitted_packages: ["android.net"],
impl_library_visibility: [
@@ -114,27 +94,32 @@
// In preparation for future move
"//packages/modules/Connectivity/apex",
"//packages/modules/Connectivity/service",
+ "//packages/modules/Connectivity/service-t",
"//frameworks/base/packages/Connectivity/service",
"//frameworks/base",
// Tests using hidden APIs
+ "//cts/tests/netlegacy22.api",
"//external/sl4a:__subpackages__",
- "//frameworks/base/tests/net:__subpackages__",
+ "//frameworks/base/packages/Connectivity/tests:__subpackages__",
"//frameworks/libs/net/common/testutils",
"//frameworks/libs/net/common/tests:__subpackages__",
"//frameworks/opt/telephony/tests/telephonytests",
+ "//packages/modules/CaptivePortalLogin/tests",
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
"//packages/modules/Connectivity/tests:__subpackages__",
"//packages/modules/NetworkStack/tests:__subpackages__",
+ "//packages/modules/Wifi/service/tests/wifitests",
],
apex_available: [
- "//apex_available:platform",
"com.android.tethering",
],
+ lint: { strict_updatability_linting: true },
}
-cc_defaults {
- name: "libframework-connectivity-defaults",
+cc_library_shared {
+ name: "libframework-connectivity-jni",
+ min_sdk_version: "30",
cflags: [
"-Wall",
"-Werror",
@@ -145,38 +130,19 @@
"-Wno-unguarded-availability",
"-Wthread-safety",
],
+ srcs: [
+ "jni/android_net_NetworkUtils.cpp",
+ "jni/onload.cpp",
+ ],
shared_libs: [
+ "libandroid",
"liblog",
"libnativehelper",
],
header_libs: [
"dnsproxyd_protocol_headers",
],
-}
-
-cc_library_static {
- name: "libconnectivityframeworkutils",
- defaults: ["libframework-connectivity-defaults"],
- srcs: [
- "jni/android_net_NetworkUtils.cpp",
- ],
- shared_libs: ["libandroid_net"],
- apex_available: [
- "//apex_available:platform",
- "com.android.tethering",
- ],
-}
-
-cc_library_shared {
- name: "libframework-connectivity-jni",
- min_sdk_version: "30",
- defaults: ["libframework-connectivity-defaults"],
- srcs: [
- "jni/android_net_NetworkUtils.cpp",
- "jni/onload.cpp",
- ],
- shared_libs: ["libandroid"],
- stl: "libc++_static",
+ stl: "none",
apex_available: [
"com.android.tethering",
],
diff --git a/framework/aidl-export/android/net/DhcpOption.aidl b/framework/aidl-export/android/net/DhcpOption.aidl
new file mode 100644
index 0000000..9ed0e62
--- /dev/null
+++ b/framework/aidl-export/android/net/DhcpOption.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable DhcpOption;
+
diff --git a/Tethering/src/android/net/util/TetheringMessageBase.java b/framework/aidl-export/android/net/ProfileNetworkPreference.aidl
similarity index 64%
copy from Tethering/src/android/net/util/TetheringMessageBase.java
copy to framework/aidl-export/android/net/ProfileNetworkPreference.aidl
index 29c0a81..d7f2402 100644
--- a/Tethering/src/android/net/util/TetheringMessageBase.java
+++ b/framework/aidl-export/android/net/ProfileNetworkPreference.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
-/**
- * This class defines Message.what base addresses for various state machine.
- */
-public class TetheringMessageBase {
- public static final int BASE_MAIN_SM = 0;
- public static final int BASE_IPSERVER = 100;
+package android.net;
-}
+parcelable ProfileNetworkPreference;
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 13ecb12..827da6d 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -70,7 +70,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
- method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
method @Deprecated public boolean getBackgroundDataSetting();
method @Nullable public android.net.Network getBoundNetworkForProcess();
method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
@@ -196,6 +196,7 @@
}
public static class DnsResolver.DnsException extends java.lang.Exception {
+ ctor public DnsResolver.DnsException(int, @Nullable Throwable);
field public final int code;
}
@@ -205,6 +206,7 @@
}
public final class IpPrefix implements android.os.Parcelable {
+ ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
method public boolean contains(@NonNull java.net.InetAddress);
method public int describeContents();
method @NonNull public java.net.InetAddress getAddress();
@@ -292,6 +294,7 @@
ctor public NetworkCapabilities(android.net.NetworkCapabilities);
method public int describeContents();
method @NonNull public int[] getCapabilities();
+ method @NonNull public int[] getEnterpriseCapabilitySubLevels();
method public int getLinkDownstreamBandwidthKbps();
method public int getLinkUpstreamBandwidthKbps();
method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
@@ -315,6 +318,7 @@
field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
field public static final int NET_CAPABILITY_MCX = 23; // 0x17
field public static final int NET_CAPABILITY_MMS = 0; // 0x0
+ field public static final int NET_CAPABILITY_MMTEL = 33; // 0x21
field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
@@ -406,6 +410,7 @@
method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
+ method @NonNull public android.net.NetworkRequest.Builder setIncludeOtherUidNetworks(boolean);
method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
}
@@ -437,11 +442,15 @@
method @NonNull public android.net.IpPrefix getDestination();
method @Nullable public java.net.InetAddress getGateway();
method @Nullable public String getInterface();
+ method public int getType();
method public boolean hasGateway();
method public boolean isDefaultRoute();
method public boolean matches(java.net.InetAddress);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR;
+ field public static final int RTN_THROW = 9; // 0x9
+ field public static final int RTN_UNICAST = 1; // 0x1
+ field public static final int RTN_UNREACHABLE = 7; // 0x7
}
public abstract class SocketKeepalive implements java.lang.AutoCloseable {
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 7fc0382..ec169b6 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -12,18 +12,25 @@
method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackForUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void replaceFirewallChain(int, @NonNull int[]);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @Deprecated public boolean requestRouteToHostAddress(int, java.net.InetAddress);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptPartialConnectivity(@NonNull android.net.Network, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptUnvalidated(@NonNull android.net.Network, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setFirewallChainEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setLegacyLockdownVpnEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreferences(@NonNull android.os.UserHandle, @NonNull java.util.List<android.net.ProfileNetworkPreference>, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void swapActiveStatsMap();
method public void systemReady();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateFirewallRule(int, int, boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateMeteredNetworkAllowList(int, boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateMeteredNetworkDenyList(int, boolean);
field public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
field public static final String ACTION_PROMPT_LOST_VALIDATION = "android.net.action.PROMPT_LOST_VALIDATION";
field public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY = "android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
@@ -38,8 +45,13 @@
field public static final int BLOCKED_REASON_LOCKDOWN_VPN = 16; // 0x10
field public static final int BLOCKED_REASON_NONE = 0; // 0x0
field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8
+ field public static final int FIREWALL_CHAIN_DOZABLE = 1; // 0x1
+ field public static final int FIREWALL_CHAIN_POWERSAVE = 3; // 0x3
+ field public static final int FIREWALL_CHAIN_RESTRICTED = 4; // 0x4
+ field public static final int FIREWALL_CHAIN_STANDBY = 2; // 0x2
field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0
field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
+ field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK = 2; // 0x2
}
public static class ConnectivityManager.NetworkCallback {
@@ -99,6 +111,15 @@
field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
}
+ public final class DhcpOption implements android.os.Parcelable {
+ ctor public DhcpOption(byte, @Nullable byte[]);
+ method public int describeContents();
+ method public byte getType();
+ method @Nullable public byte[] getValue();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpOption> CREATOR;
+ }
+
public final class NetworkAgentConfig implements android.os.Parcelable {
method @Nullable public String getSubscriberId();
method public boolean isBypassableVpn();
@@ -112,6 +133,11 @@
public final class NetworkCapabilities implements android.os.Parcelable {
method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids();
method public boolean hasForbiddenCapability(int);
+ field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1 = 1; // 0x1
+ field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2 = 2; // 0x2
+ field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3 = 3; // 0x3
+ field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4 = 4; // 0x4
+ field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5 = 5; // 0x5
field public static final long REDACT_ALL = -1L; // 0xffffffffffffffffL
field public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1L; // 0x1L
field public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 2L; // 0x2L
@@ -135,6 +161,23 @@
method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
}
+ public final class ProfileNetworkPreference implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.Integer> getExcludedUids();
+ method @NonNull public java.util.List<java.lang.Integer> getIncludedUids();
+ method public int getPreference();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ProfileNetworkPreference> CREATOR;
+ }
+
+ public static final class ProfileNetworkPreference.Builder {
+ ctor public ProfileNetworkPreference.Builder();
+ method @NonNull public android.net.ProfileNetworkPreference build();
+ method @NonNull public android.net.ProfileNetworkPreference.Builder setExcludedUids(@Nullable java.util.List<java.lang.Integer>);
+ method @NonNull public android.net.ProfileNetworkPreference.Builder setIncludedUids(@Nullable java.util.List<java.lang.Integer>);
+ method @NonNull public android.net.ProfileNetworkPreference.Builder setPreference(int);
+ }
+
public final class TestNetworkInterface implements android.os.Parcelable {
ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String);
method public int describeContents();
diff --git a/framework/api/module-lib-lint-baseline.txt b/framework/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..c7b0db5
--- /dev/null
+++ b/framework/api/module-lib-lint-baseline.txt
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+NoByteOrShort: android.net.DhcpOption#DhcpOption(byte, byte[]) parameter #0:
+ Should avoid odd sized primitives; use `int` instead of `byte` in parameter type in android.net.DhcpOption(byte type, byte[] value)
+NoByteOrShort: android.net.DhcpOption#describeContents():
+ Should avoid odd sized primitives; use `int` instead of `byte` in method android.net.DhcpOption.describeContents()
+NoByteOrShort: android.net.DhcpOption#getType():
+ Should avoid odd sized primitives; use `int` instead of `byte` in method android.net.DhcpOption.getType()
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index d1d51da..8d084e6 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -131,7 +131,6 @@
}
public final class IpPrefix implements android.os.Parcelable {
- ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public IpPrefix(@NonNull String);
}
@@ -279,6 +278,7 @@
method @Nullable public String getSsid();
method @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
method @NonNull public int[] getTransportTypes();
+ method @Nullable public java.util.List<android.net.Network> getUnderlyingNetworks();
method public boolean isPrivateDnsBroken();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
field public static final int NET_CAPABILITY_BIP = 31; // 0x1f
@@ -294,9 +294,11 @@
ctor public NetworkCapabilities.Builder();
ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
+ method @NonNull public android.net.NetworkCapabilities.Builder addEnterpriseCapabilitySubLevel(int);
method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
method @NonNull public android.net.NetworkCapabilities build();
method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
+ method @NonNull public android.net.NetworkCapabilities.Builder removeEnterpriseCapabilitySubLevel(int);
method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
@@ -309,6 +311,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+ method @NonNull public android.net.NetworkCapabilities.Builder setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method @NonNull public static android.net.NetworkCapabilities.Builder withoutDefaultCapabilities();
}
@@ -430,10 +433,6 @@
ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
method public int getMtu();
- method public int getType();
- field public static final int RTN_THROW = 9; // 0x9
- field public static final int RTN_UNICAST = 1; // 0x1
- field public static final int RTN_UNREACHABLE = 7; // 0x7
}
public abstract class SocketKeepalive implements java.lang.AutoCloseable {
diff --git a/framework/jarjar-rules-proto.txt b/framework/jarjar-rules-proto.txt
deleted file mode 100644
index 37b4dec..0000000
--- a/framework/jarjar-rules-proto.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-keep android.net.NetworkCapabilitiesProto
-keep android.net.NetworkProto
-keep android.net.NetworkRequestProto
diff --git a/framework/jarjar-rules.txt b/framework/jarjar-rules.txt
index 0959840..ae6b9c3 100644
--- a/framework/jarjar-rules.txt
+++ b/framework/jarjar-rules.txt
@@ -1,10 +1,3 @@
rule com.android.net.module.util.** android.net.connectivity.framework.util.@1
-
-# TODO (b/149403767): remove the annotations from net-utils-device-common instead of here
-zap android.annotation.**
-zap com.android.net.module.annotation.**
-zap com.android.internal.annotations.**
-
-rule android.net.NetworkCapabilitiesProto* android.net.connectivity.proto.NetworkCapabilitiesProto@1
-rule android.net.NetworkProto* android.net.connectivity.proto.NetworkProto@1
-rule android.net.NetworkRequestProto* android.net.connectivity.proto.NetworkRequestProto@1
+rule com.android.modules.utils.** android.net.connectivity.framework.modules.utils.@1
+rule android.net.NetworkFactory* android.net.connectivity.framework.NetworkFactory@1
diff --git a/framework/jni/android_net_NetworkUtils.cpp b/framework/jni/android_net_NetworkUtils.cpp
index 26484fb..7478b3e 100644
--- a/framework/jni/android_net_NetworkUtils.cpp
+++ b/framework/jni/android_net_NetworkUtils.cpp
@@ -16,34 +16,19 @@
#define LOG_TAG "NetworkUtils"
-#include <vector>
-
#include <android/file_descriptor_jni.h>
#include <android/multinetwork.h>
-#include <arpa/inet.h>
#include <linux/filter.h>
-#include <linux/if_arp.h>
#include <linux/tcp.h>
-#include <net/if.h>
-#include <netinet/ether.h>
-#include <netinet/ip.h>
-#include <netinet/udp.h>
+#include <netinet/in.h>
+#include <string.h>
#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
-#include <cutils/properties.h>
-#include <nativehelper/JNIHelp.h>
#include <nativehelper/JNIPlatformHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
#include <utils/Log.h>
-#include <utils/misc.h>
#include "jni.h"
-extern "C" {
-int ifc_enable(const char *ifname);
-int ifc_disable(const char *ifname);
-}
-
#define NETUTILS_PKG_NAME "android/net/NetworkUtils"
namespace android {
@@ -52,6 +37,9 @@
// FrameworkListener limits the size of commands to 4096 bytes.
constexpr int MAXCMDSIZE = 4096;
+static volatile jclass class_Network = 0;
+static volatile jmethodID method_fromNetworkHandle = 0;
+
static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
jclass clazz = env->FindClass(class_name);
LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
@@ -138,11 +126,11 @@
// Only allow dname which could be simply formatted to UTF8.
// In native layer, res_mkquery would re-format the input char array to packet.
- std::vector<char> queryname(byteCountUTF8 + 1, 0);
+ char queryname[byteCountUTF8 + 1];
+ memset(queryname, 0, (byteCountUTF8 + 1) * sizeof(char));
- env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname.data());
-
- int fd = android_res_nquery(netHandle, queryname.data(), ns_class, ns_type, flags);
+ env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname);
+ int fd = android_res_nquery(netHandle, queryname, ns_class, ns_type, flags);
if (fd < 0) {
jniThrowErrnoException(env, "resNetworkQuery", -fd);
@@ -170,9 +158,9 @@
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
int fd = AFileDescriptor_getFd(env, javaFd);
int rcode;
- std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
+ uint8_t buf[MAXPACKETSIZE] = {0};
- int res = android_res_nresult(fd, &rcode, buf.data(), MAXPACKETSIZE);
+ int res = android_res_nresult(fd, &rcode, buf, MAXPACKETSIZE);
jniSetFileDescriptorOfFD(env, javaFd, -1);
if (res < 0) {
jniThrowErrnoException(env, "resNetworkResult", -res);
@@ -184,8 +172,7 @@
jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
return nullptr;
} else {
- env->SetByteArrayRegion(answer, 0, res,
- reinterpret_cast<jbyte*>(buf.data()));
+ env->SetByteArrayRegion(answer, 0, res, reinterpret_cast<jbyte*>(buf));
}
jclass class_DnsResponse = env->FindClass("android/net/DnsResolver$DnsResponse");
@@ -207,11 +194,14 @@
return nullptr;
}
- static jclass class_Network = MakeGlobalRefOrDie(
- env, FindClassOrDie(env, "android/net/Network"));
- static jmethodID method = env->GetStaticMethodID(class_Network, "fromNetworkHandle",
- "(J)Landroid/net/Network;");
- return env->CallStaticObjectMethod(class_Network, method, static_cast<jlong>(dnsNetHandle));
+ if (method_fromNetworkHandle == 0) {
+ // This may be called multiple times concurrently but that is fine
+ class_Network = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/net/Network"));
+ method_fromNetworkHandle = env->GetStaticMethodID(class_Network, "fromNetworkHandle",
+ "(J)Landroid/net/Network;");
+ }
+ return env->CallStaticObjectMethod(class_Network, method_fromNetworkHandle,
+ static_cast<jlong>(dnsNetHandle));
}
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
diff --git a/framework/lint-baseline.xml b/framework/lint-baseline.xml
deleted file mode 100644
index 099202f..0000000
--- a/framework/lint-baseline.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `new android.net.ParseException`"
- errorLine1=" ParseException pe = new ParseException(e.reason, e.getCause());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/framework/src/android/net/DnsResolver.java"
- line="301"
- column="37"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"
- errorLine1=" protected class ActiveDataSubscriptionIdListener extends TelephonyCallback"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
- line="96"
- column="62"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener`"
- errorLine1=" implements TelephonyCallback.ActiveDataSubscriptionIdListener {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
- line="97"
- column="24"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"
- errorLine1=" ctx.getSystemService(TelephonyManager.class).registerTelephonyCallback("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
- line="126"
- column="54"/>
- </issue>
-
-</issues>
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index eecd12c..009890c 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -46,7 +46,6 @@
import android.net.TetheringManager.StartTetheringCallback;
import android.net.TetheringManager.TetheringEventCallback;
import android.net.TetheringManager.TetheringRequest;
-import android.net.wifi.WifiNetworkSuggestion;
import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
@@ -144,6 +143,11 @@
* <p/>
* For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
* is set to {@code true} if there are no connected networks at all.
+ * <p />
+ * Note that this broadcast is deprecated and generally tries to implement backwards
+ * compatibility with older versions of Android. As such, it may not reflect new
+ * capabilities of the system, like multiple networks being connected at the same
+ * time, the details of newer technology, or changes in tethering state.
*
* @deprecated apps should use the more versatile {@link #requestNetwork},
* {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback}
@@ -927,6 +931,48 @@
private final IConnectivityManager mService;
/**
+ * Firewall chain for device idle (doze mode).
+ * Allowlist of apps that have network access in device idle.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int FIREWALL_CHAIN_DOZABLE = 1;
+
+ /**
+ * Firewall chain used for app standby.
+ * Denylist of apps that do not have network access.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int FIREWALL_CHAIN_STANDBY = 2;
+
+ /**
+ * Firewall chain used for battery saver.
+ * Allowlist of apps that have network access when battery saver is on.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int FIREWALL_CHAIN_POWERSAVE = 3;
+
+ /**
+ * Firewall chain used for restricted networking mode.
+ * Allowlist of apps that have access in restricted networking mode.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int FIREWALL_CHAIN_RESTRICTED = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "FIREWALL_CHAIN_", value = {
+ FIREWALL_CHAIN_DOZABLE,
+ FIREWALL_CHAIN_STANDBY,
+ FIREWALL_CHAIN_POWERSAVE,
+ FIREWALL_CHAIN_RESTRICTED
+ })
+ public @interface FirewallChain {}
+
+ /**
* A kludge to facilitate static access where a Context pointer isn't available, like in the
* case of the static set/getProcessDefaultNetwork methods and from the Network class.
* TODO: Remove this after deprecating the static methods in favor of non-static methods or
@@ -1074,7 +1120,8 @@
}
/**
- * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+ * Preference for {@link ProfileNetworkPreference#setPreference(int)}.
+ * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by follow the default rules.
* @hide
*/
@@ -1082,7 +1129,8 @@
public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0;
/**
- * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
+ * Preference for {@link ProfileNetworkPreference#setPreference(int)}.
+ * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by default go on a network with
* {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}, and on the system default network
* if no such network is available.
@@ -1091,13 +1139,25 @@
@SystemApi(client = MODULE_LIBRARIES)
public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1;
+ /**
+ * Preference for {@link ProfileNetworkPreference#setPreference(int)}.
+ * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
+ * Specify that the traffic for this user should by default go on a network with
+ * {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE} and if no such network is available
+ * should not go on the system default network
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK = 2;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
PROFILE_NETWORK_PREFERENCE_DEFAULT,
- PROFILE_NETWORK_PREFERENCE_ENTERPRISE
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK
})
- public @interface ProfileNetworkPreference {
+ public @interface ProfileNetworkPreferencePolicy {
}
/**
@@ -1163,10 +1223,11 @@
* default data network. In the event that the current active default data
* network disconnects, the returned {@code Network} object will no longer
* be usable. This will return {@code null} when there is no default
- * network.
+ * network, or when the default network is blocked.
*
* @return a {@link Network} object for the current default network or
- * {@code null} if no default network is currently active
+ * {@code null} if no default network is currently active or if
+ * the default network is blocked for the caller
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@Nullable
@@ -1441,10 +1502,18 @@
* Returns an array of all {@link Network} currently tracked by the
* framework.
*
+ * @deprecated This method does not provide any notification of network state changes, forcing
+ * apps to call it repeatedly. This is inefficient and prone to race conditions.
+ * Apps should use methods such as
+ * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} instead.
+ * Apps that desire to obtain information about networks that do not apply to them
+ * can use {@link NetworkRequest.Builder#setIncludeOtherUidNetworks}.
+ *
* @return an array of {@link Network} objects.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@NonNull
+ @Deprecated
public Network[] getAllNetworks() {
try {
return mService.getAllNetworks();
@@ -2326,6 +2395,7 @@
void onNetworkActive();
}
+ @GuardedBy("mNetworkActivityListeners")
private final ArrayMap<OnNetworkActiveListener, INetworkActivityListener>
mNetworkActivityListeners = new ArrayMap<>();
@@ -2342,18 +2412,20 @@
* @param l The listener to be told when the network is active.
*/
public void addDefaultNetworkActiveListener(final OnNetworkActiveListener l) {
- INetworkActivityListener rl = new INetworkActivityListener.Stub() {
+ final INetworkActivityListener rl = new INetworkActivityListener.Stub() {
@Override
public void onNetworkActive() throws RemoteException {
l.onNetworkActive();
}
};
- try {
- mService.registerNetworkActivityListener(rl);
- mNetworkActivityListeners.put(l, rl);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mNetworkActivityListeners) {
+ try {
+ mService.registerNetworkActivityListener(rl);
+ mNetworkActivityListeners.put(l, rl);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@@ -2364,14 +2436,17 @@
* @param l Previously registered listener.
*/
public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
- INetworkActivityListener rl = mNetworkActivityListeners.get(l);
- if (rl == null) {
- throw new IllegalArgumentException("Listener was not registered.");
- }
- try {
- mService.registerNetworkActivityListener(rl);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mNetworkActivityListeners) {
+ final INetworkActivityListener rl = mNetworkActivityListeners.get(l);
+ if (rl == null) {
+ throw new IllegalArgumentException("Listener was not registered.");
+ }
+ try {
+ mService.unregisterNetworkActivityListener(rl);
+ mNetworkActivityListeners.remove(l);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@@ -2427,7 +2502,7 @@
@NonNull String callingPackage, @Nullable String callingAttributionTag,
boolean throwException) {
return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
- throwException);
+ callingAttributionTag, throwException);
}
/**
@@ -3459,7 +3534,8 @@
* {@link NetworkCapabilities#getTransportInfo()}) like {@link android.net.wifi.WifiInfo}
* contain location sensitive information.
* <li> OwnerUid (retrieved via {@link NetworkCapabilities#getOwnerUid()} is location
- * sensitive for wifi suggestor apps (i.e using {@link WifiNetworkSuggestion}).</li>
+ * sensitive for wifi suggestor apps (i.e using
+ * {@link android.net.wifi.WifiNetworkSuggestion WifiNetworkSuggestion}).</li>
* </p>
* <p>
* Note:
@@ -5441,6 +5517,8 @@
* @param listener an optional listener to listen for completion of the operation.
* @throws IllegalArgumentException if {@code profile} is not a valid user profile.
* @throws SecurityException if missing the appropriate permissions.
+ * @deprecated Use {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
+ * instead as it provides a more flexible API with more options.
* @hide
*/
// This function is for establishing per-profile default networking and can only be called by
@@ -5450,8 +5528,45 @@
@SuppressLint({"UserHandle"})
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ @Deprecated
public void setProfileNetworkPreference(@NonNull final UserHandle profile,
- @ProfileNetworkPreference final int preference,
+ @ProfileNetworkPreferencePolicy final int preference,
+ @Nullable @CallbackExecutor final Executor executor,
+ @Nullable final Runnable listener) {
+
+ ProfileNetworkPreference.Builder preferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ preferenceBuilder.setPreference(preference);
+ setProfileNetworkPreferences(profile,
+ List.of(preferenceBuilder.build()), executor, listener);
+ }
+
+ /**
+ * Set a list of default network selection policies for a user profile.
+ *
+ * Calling this API with a user handle defines the entire policy for that user handle.
+ * It will overwrite any setting previously set for the same user profile,
+ * and not affect previously set settings for other handles.
+ *
+ * Call this API with an empty list to remove settings for this user profile.
+ *
+ * See {@link ProfileNetworkPreference} for more details on each preference
+ * parameter.
+ *
+ * @param profile the user profile for which the preference is being set.
+ * @param profileNetworkPreferences the list of profile network preferences for the
+ * provided profile.
+ * @param executor an executor to execute the listener on. Optional if listener is null.
+ * @param listener an optional listener to listen for completion of the operation.
+ * @throws IllegalArgumentException if {@code profile} is not a valid user profile.
+ * @throws SecurityException if missing the appropriate permissions.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void setProfileNetworkPreferences(
+ @NonNull final UserHandle profile,
+ @NonNull List<ProfileNetworkPreference> profileNetworkPreferences,
@Nullable @CallbackExecutor final Executor executor,
@Nullable final Runnable listener) {
if (null != listener) {
@@ -5469,7 +5584,7 @@
};
}
try {
- mService.setProfileNetworkPreference(profile, preference, proxy);
+ mService.setProfileNetworkPreferences(profile, profileNetworkPreferences, proxy);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -5491,4 +5606,141 @@
public static Range<Integer> getIpSecNetIdRange() {
return new Range(TUN_INTF_NETID_START, TUN_INTF_NETID_START + TUN_INTF_NETID_RANGE - 1);
}
+
+ /**
+ * Sets whether the specified UID is allowed to use data on metered networks even when
+ * background data is restricted.
+ *
+ * @param uid uid of target app
+ * @throws IllegalStateException if updating allow list failed.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void updateMeteredNetworkAllowList(final int uid, final boolean add) {
+ try {
+ mService.updateMeteredNetworkAllowList(uid, add);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets whether the specified UID is prevented from using background data on metered networks.
+ * Takes precedence over {@link #updateMeteredNetworkAllowList}.
+ *
+ * @param uid uid of target app
+ * @throws IllegalStateException if updating deny list failed.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void updateMeteredNetworkDenyList(final int uid, final boolean add) {
+ try {
+ mService.updateMeteredNetworkDenyList(uid, add);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets a firewall rule for the specified UID on the specified chain.
+ *
+ * @param chain target chain.
+ * @param uid uid to allow/deny.
+ * @param allow whether networking is allowed or denied.
+ * @throws IllegalStateException if updating firewall rule failed.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void updateFirewallRule(@FirewallChain final int chain, final int uid,
+ final boolean allow) {
+ try {
+ mService.updateFirewallRule(chain, uid, allow);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables the specified firewall chain.
+ *
+ * @param chain target chain.
+ * @param enable whether the chain should be enabled.
+ * @throws IllegalStateException if enabling or disabling the firewall chain failed.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void setFirewallChainEnabled(@FirewallChain final int chain, final boolean enable) {
+ try {
+ mService.setFirewallChainEnabled(chain, enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Replaces the contents of the specified UID-based firewall chain.
+ *
+ * @param chain target chain to replace.
+ * @param uids The list of UIDs to be placed into chain.
+ * @throws IllegalStateException if replacing the firewall chain failed.
+ * @throws IllegalArgumentException if {@code chain} is not a valid chain.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void replaceFirewallChain(@FirewallChain final int chain, @NonNull final int[] uids) {
+ Objects.requireNonNull(uids);
+ try {
+ mService.replaceFirewallChain(chain, uids);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to change the current active network stats map.
+ * STOPSHIP: Remove this API before T sdk finalized, this API is temporary added for the
+ * NetworkStatsFactory which is platform code but will be moved into connectivity (tethering)
+ * mainline module.
+ *
+ * @throws IllegalStateException if swapping active stats map failed.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void swapActiveStatsMap() {
+ try {
+ mService.swapActiveStatsMap();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/framework/src/android/net/DhcpOption.java b/framework/src/android/net/DhcpOption.java
new file mode 100644
index 0000000..a125290
--- /dev/null
+++ b/framework/src/android/net/DhcpOption.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing an option in the DHCP protocol.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class DhcpOption implements Parcelable {
+ private final byte mType;
+ private final byte[] mValue;
+
+ /**
+ * Constructs a DhcpOption object.
+ *
+ * @param type the type of this option
+ * @param value the value of this option. If {@code null}, DHCP packets containing this option
+ * will include the option type in the Parameter Request List. Otherwise, DHCP
+ * packets containing this option will include the option in the options section.
+ */
+ public DhcpOption(byte type, @Nullable byte[] value) {
+ mType = type;
+ mValue = value;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeByte(mType);
+ dest.writeByteArray(mValue);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<DhcpOption> CREATOR =
+ new Creator<DhcpOption>() {
+ public DhcpOption createFromParcel(Parcel in) {
+ return new DhcpOption(in.readByte(), in.createByteArray());
+ }
+
+ public DhcpOption[] newArray(int size) {
+ return new DhcpOption[size];
+ }
+ };
+
+ /** Get the type of DHCP option */
+ public byte getType() {
+ return mType;
+ }
+
+ /** Get the value of DHCP option */
+ @Nullable public byte[] getValue() {
+ return mValue == null ? null : mValue.clone();
+ }
+}
diff --git a/framework/src/android/net/DnsResolver.java b/framework/src/android/net/DnsResolver.java
index dac88ad..164160f 100644
--- a/framework/src/android/net/DnsResolver.java
+++ b/framework/src/android/net/DnsResolver.java
@@ -164,7 +164,7 @@
*/
@DnsError public final int code;
- DnsException(@DnsError int code, @Nullable Throwable cause) {
+ public DnsException(@DnsError int code, @Nullable Throwable cause) {
super(cause);
this.code = code;
}
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 50ec781..df4663f 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -36,6 +36,7 @@
import android.net.NetworkState;
import android.net.NetworkStateSnapshot;
import android.net.OemNetworkPreferences;
+import android.net.ProfileNetworkPreference;
import android.net.ProxyInfo;
import android.net.UidRange;
import android.net.QosSocketInfo;
@@ -218,7 +219,8 @@
void setOemNetworkPreference(in OemNetworkPreferences preference,
in IOnCompleteListener listener);
- void setProfileNetworkPreference(in UserHandle profile, int preference,
+ void setProfileNetworkPreferences(in UserHandle profile,
+ in List<ProfileNetworkPreference> preferences,
in IOnCompleteListener listener);
int getRestrictBackgroundStatusByCaller();
@@ -228,4 +230,16 @@
void unofferNetwork(in INetworkOfferCallback callback);
void setTestAllowBadWifiUntil(long timeMs);
+
+ void updateMeteredNetworkAllowList(int uid, boolean add);
+
+ void updateMeteredNetworkDenyList(int uid, boolean add);
+
+ void updateFirewallRule(int chain, int uid, boolean allow);
+
+ void setFirewallChainEnabled(int chain, boolean enable);
+
+ void replaceFirewallChain(int chain, in int[] uids);
+
+ void swapActiveStatsMap();
}
diff --git a/framework/src/android/net/IpPrefix.java b/framework/src/android/net/IpPrefix.java
index bf4481a..c26a0b5 100644
--- a/framework/src/android/net/IpPrefix.java
+++ b/framework/src/android/net/IpPrefix.java
@@ -87,9 +87,7 @@
*
* @param address the IP address. Must be non-null.
* @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
- * @hide
*/
- @SystemApi
public IpPrefix(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength) {
// We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
// which is unnecessary because getAddress() already returns a clone.
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
index b3770ea..53f171a 100644
--- a/framework/src/android/net/Network.java
+++ b/framework/src/android/net/Network.java
@@ -382,13 +382,14 @@
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
- bindSocket(pfd.getFileDescriptor());
- // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
- // dup share the underlying socket in the kernel. The socket is never truly closed until the
- // last fd pointing to the socket being closed. So close the dup one after binding the
- // socket to control the lifetime of the dup fd.
- pfd.close();
+
+ // ParcelFileDescriptor.fromDatagramSocket() creates a dup of the original fd. The original
+ // and the dup share the underlying socket in the kernel. The socket is never truly closed
+ // until the last fd pointing to the socket being closed. Try and eventually close the dup
+ // one after binding the socket to control the lifetime of the dup fd.
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
+ bindSocket(pfd.getFileDescriptor());
+ }
}
/**
@@ -400,13 +401,13 @@
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
- bindSocket(pfd.getFileDescriptor());
- // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
- // dup share the underlying socket in the kernel. The socket is never truly closed until the
- // last fd pointing to the socket being closed. So close the dup one after binding the
- // socket to control the lifetime of the dup fd.
- pfd.close();
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and
+ // the dup share the underlying socket in the kernel. The socket is never truly closed
+ // until the last fd pointing to the socket being closed. Try and eventually close the dup
+ // one after binding the socket to control the lifetime of the dup fd.
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
+ bindSocket(pfd.getFileDescriptor());
+ }
}
/**
diff --git a/framework/src/android/net/NetworkAgentConfig.java b/framework/src/android/net/NetworkAgentConfig.java
index ad8396b..93fc379 100644
--- a/framework/src/android/net/NetworkAgentConfig.java
+++ b/framework/src/android/net/NetworkAgentConfig.java
@@ -232,6 +232,20 @@
return mLegacyExtraInfo;
}
+ /**
+ * If the {@link Network} is a VPN, whether the local traffic is exempted from the VPN.
+ * @hide
+ */
+ public boolean excludeLocalRouteVpn = false;
+
+ /**
+ * @return whether local traffic is excluded from the VPN network.
+ * @hide
+ */
+ public boolean getExcludeLocalRouteVpn() {
+ return excludeLocalRouteVpn;
+ }
+
/** @hide */
public NetworkAgentConfig() {
}
@@ -251,6 +265,7 @@
legacySubType = nac.legacySubType;
legacySubTypeName = nac.legacySubTypeName;
mLegacyExtraInfo = nac.mLegacyExtraInfo;
+ excludeLocalRouteVpn = nac.excludeLocalRouteVpn;
}
}
@@ -407,6 +422,17 @@
}
/**
+ * Sets whether the local traffic is exempted from VPN.
+ *
+ * @return this builder, to facilitate chaining.
+ * @hide TODO(184750836): Unhide once the implementation is completed.
+ */
+ public Builder setExcludeLocalRoutesVpn(boolean excludeLocalRoutes) {
+ mConfig.excludeLocalRouteVpn = excludeLocalRoutes;
+ return this;
+ }
+
+ /**
* Returns the constructed {@link NetworkAgentConfig} object.
*/
@NonNull
@@ -429,14 +455,15 @@
&& legacyType == that.legacyType
&& Objects.equals(subscriberId, that.subscriberId)
&& Objects.equals(legacyTypeName, that.legacyTypeName)
- && Objects.equals(mLegacyExtraInfo, that.mLegacyExtraInfo);
+ && Objects.equals(mLegacyExtraInfo, that.mLegacyExtraInfo)
+ && excludeLocalRouteVpn == that.excludeLocalRouteVpn;
}
@Override
public int hashCode() {
return Objects.hash(allowBypass, explicitlySelected, acceptUnvalidated,
acceptPartialConnectivity, provisioningNotificationDisabled, subscriberId,
- skip464xlat, legacyType, legacyTypeName, mLegacyExtraInfo);
+ skip464xlat, legacyType, legacyTypeName, mLegacyExtraInfo, excludeLocalRouteVpn);
}
@Override
@@ -453,6 +480,7 @@
+ ", hasShownBroken = " + hasShownBroken
+ ", legacyTypeName = '" + legacyTypeName + '\''
+ ", legacyExtraInfo = '" + mLegacyExtraInfo + '\''
+ + ", excludeLocalRouteVpn = '" + excludeLocalRouteVpn + '\''
+ "}";
}
@@ -475,6 +503,7 @@
out.writeInt(legacySubType);
out.writeString(legacySubTypeName);
out.writeString(mLegacyExtraInfo);
+ out.writeInt(excludeLocalRouteVpn ? 1 : 0);
}
public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
@@ -494,6 +523,7 @@
networkAgentConfig.legacySubType = in.readInt();
networkAgentConfig.legacySubTypeName = in.readString();
networkAgentConfig.mLegacyExtraInfo = in.readString();
+ networkAgentConfig.excludeLocalRouteVpn = in.readInt() != 0;
return networkAgentConfig;
}
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 17fc3b4..84f7cbb 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import android.annotation.IntDef;
@@ -27,7 +29,6 @@
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.wifi.WifiNetworkSuggestion;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,7 +43,10 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
@@ -129,6 +133,11 @@
// Set to true when private DNS is broken.
private boolean mPrivateDnsBroken;
+ // Underlying networks, if any. VPNs and VCNs typically have underlying networks.
+ // This is an unmodifiable list and it will be returned as is in the getter.
+ @Nullable
+ private List<Network> mUnderlyingNetworks;
+
/**
* Uid of the app making the request.
*/
@@ -139,6 +148,70 @@
*/
private String mRequestorPackageName;
+ /**
+ * enterprise capability sub level 1
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1 = 1;
+
+ /**
+ * enterprise capability sub level 2
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2 = 2;
+
+ /**
+ * enterprise capability sub level 3
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3 = 3;
+
+ /**
+ * enterprise capability sub level 4
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4 = 4;
+
+ /**
+ * enterprise capability sub level 5
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5 = 5;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "NET_CAPABILITY_ENTERPRISE_SUB_LEVEL" }, value = {
+ NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
+ NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2,
+ NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3,
+ NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4,
+ NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5,
+ })
+
+ public @interface EnterpriseCapabilitySubLevel {
+ }
+
+ /**
+ * Bitfield representing the network's enterprise capability sublevel. If any are specified
+ * they will be satisfied by any Network that matches all of them.
+ * {@see addEnterpriseCapabilitySubLevel} for details on how masks are added
+ */
+ private int mEnterpriseCapabilitySubLevel;
+
+ /**
+ * @return all the enteprise capabilities sub level set on this {@code NetworkCapability}
+ * instance.
+ *
+ */
+ public @NonNull @EnterpriseCapabilitySubLevel int[] getEnterpriseCapabilitySubLevels() {
+ return NetworkCapabilitiesUtils.unpackBits(mEnterpriseCapabilitySubLevel);
+ }
+
public NetworkCapabilities() {
clearAll();
mNetworkCapabilities = DEFAULT_CAPABILITIES;
@@ -184,6 +257,8 @@
mRequestorUid = Process.INVALID_UID;
mRequestorPackageName = null;
mSubIds = new ArraySet<>();
+ mUnderlyingNetworks = null;
+ mEnterpriseCapabilitySubLevel = 0;
}
/**
@@ -213,6 +288,10 @@
mRequestorUid = nc.mRequestorUid;
mRequestorPackageName = nc.mRequestorPackageName;
mSubIds = new ArraySet<>(nc.mSubIds);
+ // mUnderlyingNetworks is an unmodifiable list if non-null, so a defensive copy is not
+ // necessary.
+ mUnderlyingNetworks = nc.mUnderlyingNetworks;
+ mEnterpriseCapabilitySubLevel = nc.mEnterpriseCapabilitySubLevel;
}
/**
@@ -263,6 +342,7 @@
NET_CAPABILITY_VSIM,
NET_CAPABILITY_BIP,
NET_CAPABILITY_HEAD_UNIT,
+ NET_CAPABILITY_MMTEL,
})
public @interface NetCapability { }
@@ -501,8 +581,13 @@
*/
public static final int NET_CAPABILITY_HEAD_UNIT = 32;
+ /**
+ * Indicates that this network has ability to support MMTEL (Multimedia Telephony service).
+ */
+ public static final int NET_CAPABILITY_MMTEL = 33;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_HEAD_UNIT;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_MMTEL;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -699,6 +784,73 @@
}
/**
+ * Adds the given enterprise capability sub level to this {@code NetworkCapability} instance.
+ * Note that when searching for a network to satisfy a request, all capabilities sub level
+ * requested must be satisfied.
+ *
+ * @param enterpriseCapabilitySubLevel the enterprise capability sub level to be added.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ private @NonNull NetworkCapabilities addEnterpriseCapabilitySubLevel(
+ @EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+ checkValidEnterpriseCapabilitySublevel(enterpriseCapabilitySubLevel);
+ mEnterpriseCapabilitySubLevel |= 1 << enterpriseCapabilitySubLevel;
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given enterprise capability sublevel from this
+ * {@code NetworkCapability} instance that were added via addEnterpriseCapabilitySubLevel(int)
+ *
+ * @param enterpriseCapabilitySubLevel the enterprise capability sublevel to be removed.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ private @NonNull NetworkCapabilities removeEnterpriseCapabilitySubLevel(
+ @EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+ checkValidEnterpriseCapabilitySublevel(enterpriseCapabilitySubLevel);
+ final int mask = ~(1 << enterpriseCapabilitySubLevel);
+ mEnterpriseCapabilitySubLevel &= mask;
+ return this;
+ }
+
+ /**
+ * Set the underlying networks of this network.
+ *
+ * @param networks The underlying networks of this network.
+ *
+ * @hide
+ */
+ public void setUnderlyingNetworks(@Nullable List<Network> networks) {
+ mUnderlyingNetworks =
+ (networks == null) ? null : Collections.unmodifiableList(new ArrayList<>(networks));
+ }
+
+ /**
+ * Get the underlying networks of this network. If the caller is not system privileged, this is
+ * always redacted to null and it will be never useful to the caller.
+ *
+ * @return <li>If the list is null, this network hasn't declared underlying networks.</li>
+ * <li>If the list is empty, this network has declared that it has no underlying
+ * networks or it doesn't run on any of the available networks.</li>
+ * <li>The list can contain multiple underlying networks, e.g. a VPN running over
+ * multiple networks at the same time.</li>
+ *
+ * @hide
+ */
+ @SuppressLint("NullableCollection")
+ @Nullable
+ @SystemApi
+ public List<Network> getUnderlyingNetworks() {
+ return mUnderlyingNetworks;
+ }
+
+ private boolean equalsUnderlyingNetworks(@NonNull NetworkCapabilities nc) {
+ return Objects.equals(getUnderlyingNetworks(), nc.getUnderlyingNetworks());
+ }
+
+ /**
* Tests for the presence of a capability on this instance.
*
* @param capability the capabilities to be tested for.
@@ -741,18 +893,6 @@
}
}
- private void combineNetCapabilities(@NonNull NetworkCapabilities nc) {
- final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities;
- final long forbiddenCaps =
- this.mForbiddenNetworkCapabilities | nc.mForbiddenNetworkCapabilities;
- if ((wantedCaps & forbiddenCaps) != 0) {
- throw new IllegalArgumentException(
- "Cannot have the same capability in wanted and forbidden lists.");
- }
- this.mNetworkCapabilities = wantedCaps;
- this.mForbiddenNetworkCapabilities = forbiddenCaps;
- }
-
/**
* Convenience function that returns a human-readable description of the first mutable
* capability we find. Used to present an error message to apps that request mutable
@@ -775,6 +915,22 @@
return null;
}
+ private boolean equalsEnterpriseCapabilitiesSubLevel(@NonNull NetworkCapabilities nc) {
+ return nc.mEnterpriseCapabilitySubLevel == this.mEnterpriseCapabilitySubLevel;
+ }
+
+ private boolean satisfiedByEnterpriseCapabilitiesSubLevel(@NonNull NetworkCapabilities nc) {
+ final int requestedEnterpriseCapabilitiesSubLevel = mEnterpriseCapabilitySubLevel;
+ final int providedEnterpriseCapabailitiesSubLevel = nc.mEnterpriseCapabilitySubLevel;
+
+ if ((providedEnterpriseCapabailitiesSubLevel & requestedEnterpriseCapabilitiesSubLevel)
+ == requestedEnterpriseCapabilitiesSubLevel) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private boolean satisfiedByNetCapabilities(@NonNull NetworkCapabilities nc,
boolean onlyImmutable) {
long requestedCapabilities = mNetworkCapabilities;
@@ -1055,8 +1211,12 @@
return isValidTransport(transportType) && ((mTransportTypes & (1 << transportType)) != 0);
}
- private void combineTransportTypes(NetworkCapabilities nc) {
- this.mTransportTypes |= nc.mTransportTypes;
+ /**
+ * Returns true iff this NetworkCapabilities has the specified transport and no other.
+ * @hide
+ */
+ public boolean hasSingleTransport(@Transport int transportType) {
+ return mTransportTypes == (1 << transportType);
}
private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
@@ -1137,14 +1297,14 @@
*
* <p>
* This field will only be populated for VPN and wifi network suggestor apps (i.e using
- * {@link WifiNetworkSuggestion}), and only for the network they own.
- * In the case of wifi network suggestors apps, this field is also location sensitive, so the
- * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
- * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
- * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
- * callback. If the apps targets SDK version equal to {{@link Build.VERSION_CODES#R}, this field
- * will always be included. The app will be blamed for location access if this field is
- * included.
+ * {@link android.net.wifi.WifiNetworkSuggestion WifiNetworkSuggestion}), and only for the
+ * network they own. In the case of wifi network suggestors apps, this field is also location
+ * sensitive, so the app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission. If the app targets SDK version greater than or equal to
+ * {@link Build.VERSION_CODES#S}, then they also need to use
+ * {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their callback. If the
+ * apps targets SDK version equal to {{@link Build.VERSION_CODES#R}, this field will always be
+ * included. The app will be blamed for location access if this field is included.
* </p>
*/
public int getOwnerUid() {
@@ -1239,26 +1399,6 @@
}
/**
- * Combine the administrator UIDs of the capabilities.
- *
- * <p>This is only legal if either of the administrators lists are empty, or if they are equal.
- * Combining administrator UIDs is only possible for combining non-overlapping sets of UIDs.
- *
- * <p>If both administrator lists are non-empty but not equal, they conflict with each other. In
- * this case, it would not make sense to add them together.
- */
- private void combineAdministratorUids(@NonNull final NetworkCapabilities nc) {
- if (nc.mAdministratorUids.length == 0) return;
- if (mAdministratorUids.length == 0) {
- mAdministratorUids = Arrays.copyOf(nc.mAdministratorUids, nc.mAdministratorUids.length);
- return;
- }
- if (!equalsAdministratorUids(nc)) {
- throw new IllegalStateException("Can't combine two different administrator UID lists");
- }
- }
-
- /**
* Value indicating that link bandwidth is unspecified.
* @hide
*/
@@ -1320,12 +1460,6 @@
return mLinkDownBandwidthKbps;
}
- private void combineLinkBandwidths(NetworkCapabilities nc) {
- this.mLinkUpBandwidthKbps =
- Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
- this.mLinkDownBandwidthKbps =
- Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
- }
private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps
|| this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
@@ -1412,13 +1546,6 @@
return mTransportInfo;
}
- private void combineSpecifiers(NetworkCapabilities nc) {
- if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
- throw new IllegalStateException("Can't combine two networkSpecifiers");
- }
- setNetworkSpecifier(nc.mNetworkSpecifier);
- }
-
private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
return mNetworkSpecifier == null || mNetworkSpecifier.canBeSatisfiedBy(nc.mNetworkSpecifier)
|| nc.mNetworkSpecifier instanceof MatchAllNetworkSpecifier;
@@ -1428,13 +1555,6 @@
return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
}
- private void combineTransportInfos(NetworkCapabilities nc) {
- if (mTransportInfo != null && !mTransportInfo.equals(nc.mTransportInfo)) {
- throw new IllegalStateException("Can't combine two TransportInfos");
- }
- setTransportInfo(nc.mTransportInfo);
- }
-
private boolean equalsTransportInfo(NetworkCapabilities nc) {
return Objects.equals(mTransportInfo, nc.mTransportInfo);
}
@@ -1489,10 +1609,6 @@
return mSignalStrength;
}
- private void combineSignalStrength(NetworkCapabilities nc) {
- this.mSignalStrength = Math.max(this.mSignalStrength, nc.mSignalStrength);
- }
-
private boolean satisfiedBySignalStrength(NetworkCapabilities nc) {
return this.mSignalStrength <= nc.mSignalStrength;
}
@@ -1598,28 +1714,6 @@
}
/**
- * Compare if the given NetworkCapabilities have the same UIDs.
- *
- * @hide
- */
- public static boolean hasSameUids(@Nullable NetworkCapabilities nc1,
- @Nullable NetworkCapabilities nc2) {
- final Set<UidRange> uids1 = (nc1 == null) ? null : nc1.mUids;
- final Set<UidRange> uids2 = (nc2 == null) ? null : nc2.mUids;
- if (null == uids1) return null == uids2;
- if (null == uids2) return false;
- // Make a copy so it can be mutated to check that all ranges in uids2 also are in uids.
- final Set<UidRange> uids = new ArraySet<>(uids2);
- for (UidRange range : uids1) {
- if (!uids.contains(range)) {
- return false;
- }
- uids.remove(range);
- }
- return uids.isEmpty();
- }
-
- /**
* Tests if the set of UIDs that this network applies to is the same as the passed network.
* <p>
* This test only checks whether equal range objects are in both sets. It will
@@ -1629,13 +1723,13 @@
* Note that this method is not very optimized, which is fine as long as it's not used very
* often.
* <p>
- * nc is assumed nonnull.
+ * nc is assumed nonnull, else NPE.
*
* @hide
*/
@VisibleForTesting
public boolean equalsUids(@NonNull NetworkCapabilities nc) {
- return hasSameUids(nc, this);
+ return UidRange.hasSameUids(nc.mUids, mUids);
}
/**
@@ -1675,7 +1769,7 @@
* @hide
*/
@VisibleForTesting
- public boolean appliesToUidRange(@Nullable UidRange requiredRange) {
+ public boolean appliesToUidRange(@NonNull UidRange requiredRange) {
if (null == mUids) return true;
for (UidRange uidRange : mUids) {
if (uidRange.containsRange(requiredRange)) {
@@ -1686,20 +1780,6 @@
}
/**
- * Combine the UIDs this network currently applies to with the UIDs the passed
- * NetworkCapabilities apply to.
- * nc is assumed nonnull.
- */
- private void combineUids(@NonNull NetworkCapabilities nc) {
- if (null == nc.mUids || null == mUids) {
- mUids = null;
- return;
- }
- mUids.addAll(nc.mUids);
- }
-
-
- /**
* The SSID of the network, or null if not applicable or unknown.
* <p>
* This is filled in by wifi code.
@@ -1742,42 +1822,6 @@
}
/**
- * Combine SSIDs of the capabilities.
- * <p>
- * This is only legal if either the SSID of this object is null, or both SSIDs are
- * equal.
- * @hide
- */
- private void combineSSIDs(@NonNull NetworkCapabilities nc) {
- if (mSSID != null && !mSSID.equals(nc.mSSID)) {
- throw new IllegalStateException("Can't combine two SSIDs");
- }
- setSSID(nc.mSSID);
- }
-
- /**
- * Combine a set of Capabilities to this one. Useful for coming up with the complete set.
- * <p>
- * Note that this method may break an invariant of having a particular capability in either
- * wanted or forbidden lists but never in both. Requests that have the same capability in
- * both lists will never be satisfied.
- * @hide
- */
- public void combineCapabilities(@NonNull NetworkCapabilities nc) {
- combineNetCapabilities(nc);
- combineTransportTypes(nc);
- combineLinkBandwidths(nc);
- combineSpecifiers(nc);
- combineTransportInfos(nc);
- combineSignalStrength(nc);
- combineUids(nc);
- combineSSIDs(nc);
- combineRequestor(nc);
- combineAdministratorUids(nc);
- combineSubscriptionIds(nc);
- }
-
- /**
* Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
*
* @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
@@ -1792,6 +1836,7 @@
&& satisfiedByTransportTypes(nc)
&& (onlyImmutable || satisfiedByLinkBandwidths(nc))
&& satisfiedBySpecifier(nc)
+ && satisfiedByEnterpriseCapabilitiesSubLevel(nc)
&& (onlyImmutable || satisfiedBySignalStrength(nc))
&& (onlyImmutable || satisfiedByUids(nc))
&& (onlyImmutable || satisfiedBySSID(nc))
@@ -1893,7 +1938,9 @@
&& equalsPrivateDnsBroken(that)
&& equalsRequestor(that)
&& equalsAdministratorUids(that)
- && equalsSubscriptionIds(that);
+ && equalsSubscriptionIds(that)
+ && equalsUnderlyingNetworks(that)
+ && equalsEnterpriseCapabilitiesSubLevel(that);
}
@Override
@@ -1916,7 +1963,9 @@
+ Objects.hashCode(mRequestorUid) * 53
+ Objects.hashCode(mRequestorPackageName) * 59
+ Arrays.hashCode(mAdministratorUids) * 61
- + Objects.hashCode(mSubIds) * 67;
+ + Objects.hashCode(mSubIds) * 67
+ + Objects.hashCode(mUnderlyingNetworks) * 71
+ + mEnterpriseCapabilitySubLevel * 73;
}
@Override
@@ -1951,6 +2000,8 @@
dest.writeInt(mRequestorUid);
dest.writeString(mRequestorPackageName);
dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
+ dest.writeTypedList(mUnderlyingNetworks);
+ dest.writeInt(mEnterpriseCapabilitySubLevel);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1979,6 +2030,8 @@
for (int i = 0; i < subIdInts.length; i++) {
netCap.mSubIds.add(subIdInts[i]);
}
+ netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR));
+ netCap.mEnterpriseCapabilitySubLevel = in.readInt();
return netCap;
}
@Override
@@ -2070,6 +2123,25 @@
sb.append(" SubscriptionIds: ").append(mSubIds);
}
+ if (0 != mEnterpriseCapabilitySubLevel) {
+ sb.append(" EnterpriseCapabilitySublevel: ");
+ appendStringRepresentationOfBitMaskToStringBuilder(sb, mEnterpriseCapabilitySubLevel,
+ NetworkCapabilities::enterpriseCapabilitySublevelNameOf, "&");
+ }
+
+ sb.append(" UnderlyingNetworks: ");
+ if (mUnderlyingNetworks != null) {
+ sb.append("[");
+ final StringJoiner joiner = new StringJoiner(",");
+ for (int i = 0; i < mUnderlyingNetworks.size(); i++) {
+ joiner.add(mUnderlyingNetworks.get(i).toString());
+ }
+ sb.append(joiner.toString());
+ sb.append("]");
+ } else {
+ sb.append("Null");
+ }
+
sb.append("]");
return sb.toString();
}
@@ -2151,10 +2223,16 @@
case NET_CAPABILITY_VSIM: return "VSIM";
case NET_CAPABILITY_BIP: return "BIP";
case NET_CAPABILITY_HEAD_UNIT: return "HEAD_UNIT";
+ case NET_CAPABILITY_MMTEL: return "MMTEL";
default: return Integer.toString(capability);
}
}
+ private static @NonNull String enterpriseCapabilitySublevelNameOf(
+ @NetCapability int capability) {
+ return Integer.toString(capability);
+ }
+
/**
* @hide
*/
@@ -2195,6 +2273,20 @@
}
}
+ private static boolean isValidEnterpriseCapabilitySubLevel(
+ @NetworkCapabilities.EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+ return enterpriseCapabilitySubLevel >= NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1
+ && enterpriseCapabilitySubLevel <= NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5;
+ }
+
+ private static void checkValidEnterpriseCapabilitySublevel(
+ @NetworkCapabilities.EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+ if (!isValidEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel)) {
+ throw new IllegalArgumentException("enterprise capability sublevel "
+ + enterpriseCapabilitySubLevel + " is out of range");
+ }
+ }
+
/**
* Check if this {@code NetworkCapability} instance is metered.
*
@@ -2335,25 +2427,6 @@
return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
}
- /**
- * Combine requestor info of the capabilities.
- * <p>
- * This is only legal if either the requestor info of this object is reset, or both info are
- * equal.
- * nc is assumed nonnull.
- */
- private void combineRequestor(@NonNull NetworkCapabilities nc) {
- if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) {
- throw new IllegalStateException("Can't combine two uids");
- }
- if (mRequestorPackageName != null
- && !mRequestorPackageName.equals(nc.mRequestorPackageName)) {
- throw new IllegalStateException("Can't combine two package names");
- }
- setRequestorUid(nc.mRequestorUid);
- setRequestorPackageName(nc.mRequestorPackageName);
- }
-
private boolean equalsRequestor(NetworkCapabilities nc) {
return mRequestorUid == nc.mRequestorUid
&& TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
@@ -2413,20 +2486,6 @@
}
/**
- * Combine subscription ID set of the capabilities.
- *
- * <p>This is only legal if the subscription Ids are equal.
- *
- * <p>If both subscription IDs are not equal, they belong to different subscription
- * (or no subscription). In this case, it would not make sense to add them together.
- */
- private void combineSubscriptionIds(@NonNull NetworkCapabilities nc) {
- if (!Objects.equals(mSubIds, nc.mSubIds)) {
- throw new IllegalStateException("Can't combine two subscription ID sets");
- }
- }
-
- /**
* Returns a bitmask of all the applicable redactions (based on the permissions held by the
* receiving app) to be performed on this object.
*
@@ -2554,6 +2613,37 @@
}
/**
+ * Adds the given enterprise capability sub level.
+ * Note that when searching for a network to satisfy a request, all capabilities sub level
+ * requested must be satisfied. Enterprise capability sub-level is applicable only
+ * for NET_CAPABILITY_ENTERPRISE capability
+ *
+ * @param enterpriseCapabilitySubLevel enterprise capability sub-level.
+ *
+ * @return this builder
+ */
+ @NonNull
+ public Builder addEnterpriseCapabilitySubLevel(
+ @EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+ mCaps.addEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel);
+ return this;
+ }
+
+ /**
+ * Removes the given enterprise capability sub level. Enterprise capability sub-level is
+ * applicable only for NET_CAPABILITY_ENTERPRISE capability
+ *
+ * @param enterpriseCapabilitySubLevel the enterprise capability subLevel
+ * @return this builder
+ */
+ @NonNull
+ public Builder removeEnterpriseCapabilitySubLevel(
+ @EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+ mCaps.removeEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel);
+ return this;
+ }
+
+ /**
* Sets the owner UID.
*
* The default value is {@link Process#INVALID_UID}. Pass this value to reset.
@@ -2788,6 +2878,17 @@
}
/**
+ * Set the underlying networks of this network.
+ *
+ * @param networks The underlying networks of this network.
+ */
+ @NonNull
+ public Builder setUnderlyingNetworks(@Nullable List<Network> networks) {
+ mCaps.setUnderlyingNetworks(networks);
+ return this;
+ }
+
+ /**
* Builds the instance of the capabilities.
*
* @return the built instance of NetworkCapabilities.
@@ -2800,6 +2901,12 @@
+ " administrator UIDs.");
}
}
+
+ if ((mCaps.getEnterpriseCapabilitySubLevels().length != 0)
+ && !mCaps.hasCapability(NET_CAPABILITY_ENTERPRISE)) {
+ throw new IllegalStateException("Enterprise capability sublevel is applicable only"
+ + " with ENTERPRISE capability.");
+ }
return new NetworkCapabilities(mCaps);
}
}
diff --git a/framework/src/android/net/NetworkInfo.java b/framework/src/android/net/NetworkInfo.java
index bb23494..b7ec519 100644
--- a/framework/src/android/net/NetworkInfo.java
+++ b/framework/src/android/net/NetworkInfo.java
@@ -24,6 +24,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
import java.util.EnumMap;
@@ -179,21 +180,23 @@
/** {@hide} */
@UnsupportedAppUsage
- public NetworkInfo(NetworkInfo source) {
- if (source != null) {
- synchronized (source) {
- mNetworkType = source.mNetworkType;
- mSubtype = source.mSubtype;
- mTypeName = source.mTypeName;
- mSubtypeName = source.mSubtypeName;
- mState = source.mState;
- mDetailedState = source.mDetailedState;
- mReason = source.mReason;
- mExtraInfo = source.mExtraInfo;
- mIsFailover = source.mIsFailover;
- mIsAvailable = source.mIsAvailable;
- mIsRoaming = source.mIsRoaming;
- }
+ public NetworkInfo(@NonNull NetworkInfo source) {
+ // S- didn't use to crash when passing null. This plants a timebomb where mState and
+ // some other fields are null, but there may be existing code that relies on this behavior
+ // and doesn't trip the timebomb, so on SdkLevel < T, keep the old behavior. b/145972387
+ if (null == source && !SdkLevel.isAtLeastT()) return;
+ synchronized (source) {
+ mNetworkType = source.mNetworkType;
+ mSubtype = source.mSubtype;
+ mTypeName = source.mTypeName;
+ mSubtypeName = source.mSubtypeName;
+ mState = source.mState;
+ mDetailedState = source.mDetailedState;
+ mReason = source.mReason;
+ mExtraInfo = source.mExtraInfo;
+ mIsFailover = source.mIsFailover;
+ mIsAvailable = source.mIsAvailable;
+ mIsRoaming = source.mIsRoaming;
}
}
@@ -479,7 +482,7 @@
* @param detailedState the {@link DetailedState}.
* @param reason a {@code String} indicating the reason for the state change,
* if one was supplied. May be {@code null}.
- * @param extraInfo an optional {@code String} providing addditional network state
+ * @param extraInfo an optional {@code String} providing additional network state
* information passed up from the lower networking layers.
* @deprecated Use {@link NetworkCapabilities} instead.
*/
@@ -491,6 +494,12 @@
this.mState = stateMap.get(detailedState);
this.mReason = reason;
this.mExtraInfo = extraInfo;
+ // Catch both the case where detailedState is null and the case where it's some
+ // unknown value. This is clearly incorrect usage, but S- didn't use to crash (at
+ // least immediately) so keep the old behavior on older frameworks for safety.
+ if (null == mState && SdkLevel.isAtLeastT()) {
+ throw new NullPointerException("Unknown DetailedState : " + detailedState);
+ }
}
}
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
index e6a96ef..afc76d6 100644
--- a/framework/src/android/net/NetworkRequest.java
+++ b/framework/src/android/net/NetworkRequest.java
@@ -288,10 +288,15 @@
}
/**
- * Set the watched UIDs for this request. This will be reset and wiped out unless
- * the calling app holds the CHANGE_NETWORK_STATE permission.
+ * Sets this request to match only networks that apply to the specified UIDs.
*
- * @param uids The watched UIDs as a set of {@code Range<Integer>}, or null for everything.
+ * By default, the set of UIDs is the UID of the calling app, and this request will match
+ * any network that applies to the app. Setting it to {@code null} will observe any
+ * network on the system, even if it does not apply to this app. In this case, any
+ * {@link NetworkSpecifier} set on this request will be redacted or removed to prevent the
+ * application deducing restricted information such as location.
+ *
+ * @param uids The UIDs as a set of {@code Range<Integer>}, or null for everything.
* @return The builder to facilitate chaining.
* @hide
*/
@@ -522,6 +527,30 @@
mNetworkCapabilities.setSubscriptionIds(subIds);
return this;
}
+
+ /**
+ * Specifies whether the built request should also match networks that do not apply to the
+ * calling UID.
+ *
+ * By default, the built request will only match networks that apply to the calling UID.
+ * If this method is called with {@code true}, the built request will match any network on
+ * the system that matches the other parameters of the request. In this case, any
+ * information in the built request that is subject to redaction for security or privacy
+ * purposes, such as a {@link NetworkSpecifier}, will be redacted or removed to prevent the
+ * application deducing sensitive information.
+ *
+ * @param include Whether to match networks that do not apply to the calling UID.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setIncludeOtherUidNetworks(boolean include) {
+ if (include) {
+ mNetworkCapabilities.setUids(null);
+ } else {
+ mNetworkCapabilities.setSingleUid(Process.myUid());
+ }
+ return this;
+ }
}
// implement the Parcelable interface
@@ -670,25 +699,6 @@
", " + networkCapabilities.toString() + " ]";
}
- private int typeToProtoEnum(Type t) {
- switch (t) {
- case NONE:
- return NetworkRequestProto.TYPE_NONE;
- case LISTEN:
- return NetworkRequestProto.TYPE_LISTEN;
- case TRACK_DEFAULT:
- return NetworkRequestProto.TYPE_TRACK_DEFAULT;
- case REQUEST:
- return NetworkRequestProto.TYPE_REQUEST;
- case BACKGROUND_REQUEST:
- return NetworkRequestProto.TYPE_BACKGROUND_REQUEST;
- case TRACK_SYSTEM_DEFAULT:
- return NetworkRequestProto.TYPE_TRACK_SYSTEM_DEFAULT;
- default:
- return NetworkRequestProto.TYPE_UNKNOWN;
- }
- }
-
public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkRequest == false) return false;
NetworkRequest that = (NetworkRequest)obj;
diff --git a/framework/src/android/net/NetworkUtils.java b/framework/src/android/net/NetworkUtils.java
index 16a49bc..2679b62 100644
--- a/framework/src/android/net/NetworkUtils.java
+++ b/framework/src/android/net/NetworkUtils.java
@@ -42,6 +42,9 @@
* {@hide}
*/
public class NetworkUtils {
+ static {
+ System.loadLibrary("framework-connectivity-jni");
+ }
private static final String TAG = "NetworkUtils";
diff --git a/framework/src/android/net/ProfileNetworkPreference.java b/framework/src/android/net/ProfileNetworkPreference.java
new file mode 100644
index 0000000..0571b36
--- /dev/null
+++ b/framework/src/android/net/ProfileNetworkPreference.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.ConnectivityManager.ProfileNetworkPreferencePolicy;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Network preferences to be set for the user profile
+ * {@link ProfileNetworkPreferencePolicy}.
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+public final class ProfileNetworkPreference implements Parcelable {
+ private final @ProfileNetworkPreferencePolicy int mPreference;
+ private final List<Integer> mIncludedUids;
+ private final List<Integer> mExcludedUids;
+
+ private ProfileNetworkPreference(int preference, List<Integer> includedUids,
+ List<Integer> excludedUids) {
+ mPreference = preference;
+ if (includedUids != null) {
+ mIncludedUids = new ArrayList<>(includedUids);
+ } else {
+ mIncludedUids = new ArrayList<>();
+ }
+
+ if (excludedUids != null) {
+ mExcludedUids = new ArrayList<>(excludedUids);
+ } else {
+ mExcludedUids = new ArrayList<>();
+ }
+ }
+
+ private ProfileNetworkPreference(Parcel in) {
+ mPreference = in.readInt();
+ mIncludedUids = in.readArrayList(Integer.class.getClassLoader());
+ mExcludedUids = in.readArrayList(Integer.class.getClassLoader());
+ }
+
+ public int getPreference() {
+ return mPreference;
+ }
+
+ /**
+ * Get the list of UIDs subject to this preference.
+ *
+ * Included UIDs and Excluded UIDs can't both be non-empty.
+ * if both are empty, it means this request applies to all uids in the user profile.
+ * if included is not empty, then only included UIDs are applied.
+ * if excluded is not empty, then it is all uids in the user profile except these UIDs.
+ * @return List of uids included for the profile preference.
+ * {@see #getExcludedUids()}
+ */
+ public @NonNull List<Integer> getIncludedUids() {
+ return new ArrayList<>(mIncludedUids);
+ }
+
+ /**
+ * Get the list of UIDS excluded from this preference.
+ *
+ * <ul>Included UIDs and Excluded UIDs can't both be non-empty.</ul>
+ * <ul>If both are empty, it means this request applies to all uids in the user profile.</ul>
+ * <ul>If included is not empty, then only included UIDs are applied.</ul>
+ * <ul>If excluded is not empty, then it is all uids in the user profile except these UIDs.</ul>
+ * @return List of uids not included for the profile preference.
+ * {@see #getIncludedUids()}
+ */
+ public @NonNull List<Integer> getExcludedUids() {
+ return new ArrayList<>(mExcludedUids);
+ }
+
+ @Override
+ public String toString() {
+ return "ProfileNetworkPreference{"
+ + "mPreference=" + getPreference()
+ + "mIncludedUids=" + mIncludedUids.toString()
+ + "mExcludedUids=" + mExcludedUids.toString()
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final ProfileNetworkPreference that = (ProfileNetworkPreference) o;
+ return mPreference == that.mPreference
+ && (Objects.equals(mIncludedUids, that.mIncludedUids))
+ && (Objects.equals(mExcludedUids, that.mExcludedUids));
+ }
+
+ @Override
+ public int hashCode() {
+ return mPreference
+ + (Objects.hashCode(mIncludedUids) * 11)
+ + (Objects.hashCode(mExcludedUids) * 13);
+ }
+
+ /**
+ * Builder used to create {@link ProfileNetworkPreference} objects.
+ * Specify the preferred Network preference
+ */
+ public static final class Builder {
+ private @ProfileNetworkPreferencePolicy int mPreference =
+ PROFILE_NETWORK_PREFERENCE_DEFAULT;
+ private @NonNull List<Integer> mIncludedUids = new ArrayList<>();
+ private @NonNull List<Integer> mExcludedUids = new ArrayList<>();
+
+ /**
+ * Constructs an empty Builder with PROFILE_NETWORK_PREFERENCE_DEFAULT profile preference
+ */
+ public Builder() {}
+
+ /**
+ * Set the profile network preference
+ * See the documentation for the individual preferences for a description of the supported
+ * behaviors. Default value is PROFILE_NETWORK_PREFERENCE_DEFAULT.
+ * @param preference the desired network preference to use
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setPreference(@ProfileNetworkPreferencePolicy int preference) {
+ mPreference = preference;
+ return this;
+ }
+
+ /**
+ * This is a list of uids for which profile perefence is set.
+ * Null would mean that this preference applies to all uids in the profile.
+ * {@see #setExcludedUids(List<Integer>)}
+ * Included UIDs and Excluded UIDs can't both be non-empty.
+ * if both are empty, it means this request applies to all uids in the user profile.
+ * if included is not empty, then only included UIDs are applied.
+ * if excluded is not empty, then it is all uids in the user profile except these UIDs.
+ * @param uids list of uids that are included
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setIncludedUids(@Nullable List<Integer> uids) {
+ if (uids != null) {
+ mIncludedUids = new ArrayList<Integer>(uids);
+ } else {
+ mIncludedUids = new ArrayList<Integer>();
+ }
+ return this;
+ }
+
+
+ /**
+ * This is a list of uids that are excluded for the profile perefence.
+ * {@see #setIncludedUids(List<Integer>)}
+ * Included UIDs and Excluded UIDs can't both be non-empty.
+ * if both are empty, it means this request applies to all uids in the user profile.
+ * if included is not empty, then only included UIDs are applied.
+ * if excluded is not empty, then it is all uids in the user profile except these UIDs.
+ * @param uids list of uids that are not included
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setExcludedUids(@Nullable List<Integer> uids) {
+ if (uids != null) {
+ mExcludedUids = new ArrayList<Integer>(uids);
+ } else {
+ mExcludedUids = new ArrayList<Integer>();
+ }
+ return this;
+ }
+
+ /**
+ * Returns an instance of {@link ProfileNetworkPreference} created from the
+ * fields set on this builder.
+ */
+ @NonNull
+ public ProfileNetworkPreference build() {
+ if (mIncludedUids.size() > 0 && mExcludedUids.size() > 0) {
+ throw new IllegalArgumentException("Both includedUids and excludedUids "
+ + "cannot be nonempty");
+ }
+ return new ProfileNetworkPreference(mPreference, mIncludedUids, mExcludedUids);
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ dest.writeInt(mPreference);
+ dest.writeList(mIncludedUids);
+ dest.writeList(mExcludedUids);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<ProfileNetworkPreference> CREATOR =
+ new Creator<ProfileNetworkPreference>() {
+ @Override
+ public ProfileNetworkPreference[] newArray(int size) {
+ return new ProfileNetworkPreference[size];
+ }
+
+ @Override
+ public ProfileNetworkPreference createFromParcel(
+ @NonNull android.os.Parcel in) {
+ return new ProfileNetworkPreference(in);
+ }
+ };
+}
diff --git a/framework/src/android/net/ProxyInfo.java b/framework/src/android/net/ProxyInfo.java
index 745e20f..0deda37 100644
--- a/framework/src/android/net/ProxyInfo.java
+++ b/framework/src/android/net/ProxyInfo.java
@@ -37,8 +37,9 @@
* Apache HTTP stack. So {@link URLConnection} and Apache's {@code HttpClient} will use
* them automatically.
*
- * Other HTTP stacks will need to obtain the proxy info from
- * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}.
+ * Other HTTP stacks will need to obtain the proxy info by watching for the
+ * {@link Proxy#PROXY_CHANGE_ACTION} broadcast and calling methods such as
+ * {@link android.net.ConnectivityManager#getDefaultProxy}.
*/
public class ProxyInfo implements Parcelable {
diff --git a/framework/src/android/net/RouteInfo.java b/framework/src/android/net/RouteInfo.java
index fad3144..df5f151 100644
--- a/framework/src/android/net/RouteInfo.java
+++ b/framework/src/android/net/RouteInfo.java
@@ -86,16 +86,26 @@
private final String mInterface;
- /** Unicast route. @hide */
- @SystemApi
+ /**
+ * Unicast route.
+ *
+ * Indicates that destination is reachable directly or via gateway.
+ **/
public static final int RTN_UNICAST = 1;
- /** Unreachable route. @hide */
- @SystemApi
+ /**
+ * Unreachable route.
+ *
+ * Indicates that destination is unreachable.
+ **/
public static final int RTN_UNREACHABLE = 7;
- /** Throw route. @hide */
- @SystemApi
+ /**
+ * Throw route.
+ *
+ * Indicates that routing information about this destination is not in this table.
+ * Routing lookup should continue in another table.
+ **/
public static final int RTN_THROW = 9;
/**
@@ -391,10 +401,7 @@
* Retrieves the type of this route.
*
* @return The type of this route; one of the {@code RTN_xxx} constants defined in this class.
- *
- * @hide
*/
- @SystemApi
@RouteType
public int getType() {
return mType;
diff --git a/framework/src/android/net/UidRange.java b/framework/src/android/net/UidRange.java
index bd33292..a1f64f2 100644
--- a/framework/src/android/net/UidRange.java
+++ b/framework/src/android/net/UidRange.java
@@ -180,4 +180,24 @@
}
return uids;
}
+
+ /**
+ * Compare if the given UID range sets have the same UIDs.
+ *
+ * @hide
+ */
+ public static boolean hasSameUids(@Nullable Set<UidRange> uids1,
+ @Nullable Set<UidRange> uids2) {
+ if (null == uids1) return null == uids2;
+ if (null == uids2) return false;
+ // Make a copy so it can be mutated to check that all ranges in uids2 also are in uids.
+ final Set<UidRange> remainingUids = new ArraySet<>(uids2);
+ for (UidRange range : uids1) {
+ if (!remainingUids.contains(range)) {
+ return false;
+ }
+ remainingUids.remove(range);
+ }
+ return remainingUids.isEmpty();
+ }
}
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 3e7cb80..c1790c9 100644
--- a/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -20,6 +20,7 @@
import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
import android.annotation.NonNull;
+import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -29,6 +30,7 @@
import android.database.ContentObserver;
import android.net.ConnectivityResources;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -92,8 +94,8 @@
}
}
}
-
- @VisibleForTesting
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+ @VisibleForTesting @TargetApi(Build.VERSION_CODES.S)
protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
implements TelephonyCallback.ActiveDataSubscriptionIdListener {
@Override
@@ -107,6 +109,8 @@
this(ctx, handler, null);
}
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+ @TargetApi(Build.VERSION_CODES.S)
public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
mContext = ctx;
mResources = new ConnectivityResources(ctx);
diff --git a/service-t/Android.bp b/service-t/Android.bp
new file mode 100644
index 0000000..48c74c6
--- /dev/null
+++ b/service-t/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// This builds T+ services depending on framework-connectivity-tiramisu
+// hidden symbols separately from the S+ services, to ensure that S+
+// services cannot accidentally depend on T+ hidden symbols from
+// framework-connectivity-tiramisu.
+java_library {
+ name: "service-connectivity-tiramisu-pre-jarjar",
+ sdk_version: "system_server_current",
+ // TODO(b/210962470): Bump this to at least S, and then T.
+ min_sdk_version: "30",
+ srcs: [
+ "src/**/*.java",
+ // TODO: This is necessary just for LocalLog, remove after removing NativeDaemonConnector.
+ ":framework-connectivity-shared-srcs",
+ ":services.connectivity-tiramisu-updatable-sources",
+ ],
+ libs: [
+ "framework-annotations-lib",
+ "framework-connectivity.impl",
+ "framework-connectivity-tiramisu.impl",
+ "service-connectivity-pre-jarjar",
+ "unsupportedappusage",
+ ],
+ static_libs: [
+ "modules-utils-build",
+ "modules-utils-statemachine",
+ "net-utils-framework-common",
+ ],
+ apex_available: [
+ "com.android.tethering",
+ ],
+ visibility: [
+ "//packages/modules/Connectivity/service",
+ "//packages/modules/Connectivity/tests:__subpackages__",
+ ],
+}
diff --git a/service/src/com/android/server/ConnectivityServiceInitializer.java b/service-t/src/com/android/server/ConnectivityServiceInitializer.java
similarity index 68%
rename from service/src/com/android/server/ConnectivityServiceInitializer.java
rename to service-t/src/com/android/server/ConnectivityServiceInitializer.java
index 2465479..23d8bdc 100644
--- a/service/src/com/android/server/ConnectivityServiceInitializer.java
+++ b/service-t/src/com/android/server/ConnectivityServiceInitializer.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.util.Log;
+import com.android.modules.utils.build.SdkLevel;
+
/**
* Connectivity service initializer for core networking. This is called by system server to create
* a new instance of ConnectivityService.
@@ -26,13 +28,14 @@
public final class ConnectivityServiceInitializer extends SystemService {
private static final String TAG = ConnectivityServiceInitializer.class.getSimpleName();
private final ConnectivityService mConnectivity;
+ private final NsdService mNsdService;
public ConnectivityServiceInitializer(Context context) {
super(context);
// Load JNI libraries used by ConnectivityService and its dependencies
System.loadLibrary("service-connectivity");
- // TODO: Define formal APIs to get the needed services.
mConnectivity = new ConnectivityService(context);
+ mNsdService = createNsdService(context);
}
@Override
@@ -40,5 +43,20 @@
Log.i(TAG, "Registering " + Context.CONNECTIVITY_SERVICE);
publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
/* allowIsolated= */ false);
+ if (mNsdService != null) {
+ Log.i(TAG, "Registering " + Context.NSD_SERVICE);
+ publishBinderService(Context.NSD_SERVICE, mNsdService, /* allowIsolated= */ false);
+ }
+ }
+
+ /** Return NsdService instance or null if current SDK is lower than T */
+ private NsdService createNsdService(final Context context) {
+ if (!SdkLevel.isAtLeastT()) return null;
+ try {
+ return NsdService.create(context);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Unable to get NSD service", e);
+ return null;
+ }
}
}
diff --git a/service/Android.bp b/service/Android.bp
index 911d67f..e376ff7 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -19,6 +19,33 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// The library name match the service-connectivity jarjar rules that put the JNI utils in the
+// com.android.connectivity.com.android.net.module.util package.
+cc_library_shared {
+ name: "libcom_android_connectivity_com_android_net_module_util_jni",
+ min_sdk_version: "30",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+ srcs: [
+ "jni/com_android_net_module_util/onload.cpp",
+ ],
+ stl: "libc++_static",
+ static_libs: [
+ "libnet_utils_device_common_bpfjni",
+ ],
+ shared_libs: [
+ "liblog",
+ "libnativehelper",
+ ],
+ apex_available: [
+ "com.android.tethering",
+ ],
+}
+
cc_library_shared {
name: "libservice-connectivity",
min_sdk_version: "30",
@@ -30,12 +57,18 @@
],
srcs: [
"jni/com_android_server_TestNetworkService.cpp",
+ "jni/com_android_server_connectivity_ClatCoordinator.cpp",
"jni/onload.cpp",
],
stl: "libc++_static",
header_libs: [
"libbase_headers",
],
+ static_libs: [
+ "libclat",
+ "libip_checksum",
+ "libnetjniutils",
+ ],
shared_libs: [
"liblog",
"libnativehelper",
@@ -67,8 +100,9 @@
static_libs: [
"dnsresolver_aidl_interface-V9-java",
"modules-utils-build",
- "modules-utils-os",
+ "modules-utils-shell-command-handler",
"net-utils-device-common",
+ "net-utils-device-common-bpf",
"net-utils-device-common-netlink",
"net-utils-framework-common",
"netd-client",
@@ -79,6 +113,11 @@
apex_available: [
"com.android.tethering",
],
+ lint: { strict_updatability_linting: true },
+ visibility: [
+ "//packages/modules/Connectivity/service-t",
+ "//packages/modules/Connectivity/tests:__subpackages__",
+ ],
}
java_library {
@@ -95,6 +134,7 @@
apex_available: [
"com.android.tethering",
],
+ lint: { strict_updatability_linting: true },
}
java_library {
@@ -102,13 +142,19 @@
sdk_version: "system_server_current",
min_sdk_version: "30",
installable: true,
+ // This library combines system server jars that have access to different bootclasspath jars.
+ // Lower SDK service jars must not depend on higher SDK jars as that would let them
+ // transitively depend on the wrong bootclasspath jars. Sources also cannot be added here as
+ // they would transitively depend on bootclasspath jars that may not be available.
static_libs: [
"service-connectivity-pre-jarjar",
+ "service-connectivity-tiramisu-pre-jarjar",
],
jarjar_rules: "jarjar-rules.txt",
apex_available: [
"com.android.tethering",
],
+ lint: { strict_updatability_linting: true },
}
filegroup {
@@ -116,3 +162,11 @@
srcs: ["jarjar-rules.txt"],
visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
+
+// TODO: This filegroup temporary exposes for NetworkStats. It should be
+// removed right after NetworkStats moves into mainline module.
+filegroup {
+ name: "traffic-controller-utils",
+ srcs: ["src/com/android/server/BpfNetMaps.java"],
+ visibility: ["//packages/modules/Connectivity:__subpackages__"],
+}
diff --git a/service/ServiceConnectivityResources/res/values-af/strings.xml b/service/ServiceConnectivityResources/res/values-af/strings.xml
index 68720d5..086c6e3 100644
--- a/service/ServiceConnectivityResources/res/values-af/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-af/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Meld aan by Wi-Fi-netwerk"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Meld by netwerk aan"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het geen internettoegang nie"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tik vir opsies"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Selnetwerk het nie internettoegang nie"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netwerk het nie internettoegang nie"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het beperkte konnektiwiteit"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tik om in elk geval te koppel"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Het oorgeskakel na <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Toestel gebruik <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internettoegang het nie. Heffings kan geld."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Het oorgeskakel van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"\'n onbekende netwerktipe"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Stelselkonnektiwiteithulpbronne"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Meld aan by Wi-Fi-netwerk"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Meld by netwerk aan"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het geen internettoegang nie"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tik vir opsies"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Selnetwerk het nie internettoegang nie"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Netwerk het nie internettoegang nie"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het beperkte konnektiwiteit"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tik om in elk geval te koppel"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Het oorgeskakel na <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Toestel gebruik <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internettoegang het nie. Heffings kan geld."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Het oorgeskakel van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiele data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobiele data"</item>
+ <item msgid="5624324321165953608">"Wi-fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"\'n onbekende netwerktipe"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-am/strings.xml b/service/ServiceConnectivityResources/res/values-am/strings.xml
index 78d9283..886b353 100644
--- a/service/ServiceConnectivityResources/res/values-am/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-am/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ወደ Wi-Fi አውታረ መረብ በመለያ ግባ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ወደ አውታረ መረብ በመለያ ይግቡ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ምንም የበይነ መረብ መዳረሻ የለም"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ለአማራጮች መታ ያድርጉ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"የተንቀሳቃሽ ስልክ አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> የተገደበ ግንኙነት አለው"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ለማንኛውም ለማገናኘት መታ ያድርጉ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"ወደ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ተቀይሯል"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ምንም ዓይነት የበይነመረብ ግንኙነት በማይኖረው ጊዜ መሣሪያዎች <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ን ይጠቀማሉ። ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ።"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"ከ<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ወደ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ተቀይሯል"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"አንድ ያልታወቀ አውታረ መረብ ዓይነት"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"የስርዓት ግንኙነት መርጃዎች"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ወደ Wi-Fi አውታረ መረብ በመለያ ግባ"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ወደ አውታረ መረብ በመለያ ይግቡ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ምንም የበይነ መረብ መዳረሻ የለም"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"ለአማራጮች መታ ያድርጉ"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"የተንቀሳቃሽ ስልክ አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> የተገደበ ግንኙነት አለው"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"ለማንኛውም ለማገናኘት መታ ያድርጉ"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"ወደ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ተቀይሯል"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ምንም ዓይነት የበይነመረብ ግንኙነት በማይኖረው ጊዜ መሣሪያዎች <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ን ይጠቀማሉ። ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ።"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"ከ<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ወደ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ተቀይሯል"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"የተንቀሳቃሽ ስልክ ውሂብ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ብሉቱዝ"</item>
- <item msgid="1616528372438698248">"ኤተርኔት"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"የተንቀሳቃሽ ስልክ ውሂብ"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"ብሉቱዝ"</item>
+ <item msgid="346574747471703768">"ኢተርኔት"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"አንድ ያልታወቀ አውታረ መረብ ዓይነት"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ar/strings.xml b/service/ServiceConnectivityResources/res/values-ar/strings.xml
index 8698a7e..07d9c2e 100644
--- a/service/ServiceConnectivityResources/res/values-ar/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ar/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"تسجيل الدخول إلى شبكة Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"تسجيل الدخول إلى الشبكة"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"لا يتوفّر في <xliff:g id="NETWORK_SSID">%1$s</xliff:g> إمكانية الاتصال بالإنترنت."</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"انقر للحصول على الخيارات."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"شبكة الجوّال هذه غير متصلة بالإنترنت"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"الشبكة غير متصلة بالإنترنت"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"إمكانية اتصال <xliff:g id="NETWORK_SSID">%1$s</xliff:g> محدودة."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"يمكنك النقر للاتصال على أي حال."</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"تم التبديل إلى <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"يستخدم الجهاز <xliff:g id="NEW_NETWORK">%1$s</xliff:g> عندما لا يتوفر اتصال بالإنترنت في شبكة <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>، ويمكن أن يتم فرض رسوم مقابل ذلك."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"تم التبديل من <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> إلى <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نوع شبكة غير معروف"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"مصادر إمكانية اتصال الخادم"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"تسجيل الدخول إلى شبكة Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"تسجيل الدخول إلى الشبكة"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"لا يتوفّر في <xliff:g id="NETWORK_SSID">%1$s</xliff:g> إمكانية الاتصال بالإنترنت."</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"انقر للحصول على الخيارات."</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"شبكة الجوّال هذه غير متصلة بالإنترنت"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"الشبكة غير متصلة بالإنترنت"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"إمكانية اتصال <xliff:g id="NETWORK_SSID">%1$s</xliff:g> محدودة."</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"يمكنك النقر للاتصال على أي حال."</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"تم التبديل إلى <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"يستخدم الجهاز <xliff:g id="NEW_NETWORK">%1$s</xliff:g> عندما لا يتوفر اتصال بالإنترنت في شبكة <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>، ويمكن أن يتم فرض رسوم مقابل ذلك."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"تم التبديل من <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> إلى <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"بيانات الجوّال"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"بلوتوث"</item>
- <item msgid="1616528372438698248">"إيثرنت"</item>
- <item msgid="9177085807664964627">"شبكة افتراضية خاصة (VPN)"</item>
+ <item msgid="3004933964374161223">"بيانات الجوّال"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"بلوتوث"</item>
+ <item msgid="346574747471703768">"إيثرنت"</item>
+ <item msgid="5734728378097476003">"شبكة افتراضية خاصة (VPN)"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"نوع شبكة غير معروف"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-as/strings.xml b/service/ServiceConnectivityResources/res/values-as/strings.xml
index 10b234a..e753cb3 100644
--- a/service/ServiceConnectivityResources/res/values-as/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-as/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"অধিক বিকল্পৰ বাবে টিপক"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ম’বাইল নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ সকলো সেৱাৰ এক্সেছ নাই"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"যিকোনো প্ৰকাৰে সংযোগ কৰিবলৈ টিপক"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ’ল"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"যেতিয়া <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ’ল"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"অজ্ঞাত প্ৰকাৰৰ নেটৱৰ্ক"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ছিষ্টেম সংযোগৰ উৎস"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"অধিক বিকল্পৰ বাবে টিপক"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"ম’বাইল নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ সকলো সেৱাৰ এক্সেছ নাই"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"যিকোনো প্ৰকাৰে সংযোগ কৰিবলৈ টিপক"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ’ল"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"যেতিয়া <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ’ল"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ম’বাইল ডেটা"</item>
- <item msgid="5520925862115353992">"ৱাই-ফাই"</item>
- <item msgid="1055487873974272842">"ব্লুটুথ"</item>
- <item msgid="1616528372438698248">"ইথাৰনেট"</item>
- <item msgid="9177085807664964627">"ভিপিএন"</item>
+ <item msgid="3004933964374161223">"ম’বাইল ডেটা"</item>
+ <item msgid="5624324321165953608">"ৱাই-ফাই"</item>
+ <item msgid="5667906231066981731">"ব্লুটুথ"</item>
+ <item msgid="346574747471703768">"ইথাৰনেট"</item>
+ <item msgid="5734728378097476003">"ভিপিএন"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"অজ্ঞাত প্ৰকাৰৰ নেটৱৰ্ক"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-az/strings.xml b/service/ServiceConnectivityResources/res/values-az/strings.xml
index d75a204..f33a3e3 100644
--- a/service/ServiceConnectivityResources/res/values-az/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-az/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi şəbəkəsinə daxil ol"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Şəbəkəyə daxil olun"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> üçün internet girişi əlçatan deyil"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Seçimlər üçün tıklayın"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil şəbəkənin internetə girişi yoxdur"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Şəbəkənin internetə girişi yoxdur"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Özəl DNS serverinə giriş mümkün deyil"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> bağlantını məhdudlaşdırdı"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"İstənilən halda klikləyin"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> şəbəkə növünə keçirildi"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> şəbəkəsinin internetə girişi olmadıqda, cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> şəbəkəsini istifadə edir. Xidmət haqqı tutula bilər."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> şəbəkəsindən <xliff:g id="NEW_NETWORK">%2$s</xliff:g> şəbəkəsinə keçirildi"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"naməlum şəbəkə növü"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Sistem Bağlantı Resursları"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi şəbəkəsinə daxil ol"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Şəbəkəyə daxil olun"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> üçün internet girişi əlçatan deyil"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Seçimlər üçün tıklayın"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobil şəbəkənin internetə girişi yoxdur"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Şəbəkənin internetə girişi yoxdur"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Özəl DNS serverinə giriş mümkün deyil"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> bağlantını məhdudlaşdırdı"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"İstənilən halda klikləyin"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> şəbəkə növünə keçirildi"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> şəbəkəsinin internetə girişi olmadıqda, cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> şəbəkəsini istifadə edir. Xidmət haqqı tutula bilər."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> şəbəkəsindən <xliff:g id="NEW_NETWORK">%2$s</xliff:g> şəbəkəsinə keçirildi"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobil data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobil data"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"naməlum şəbəkə növü"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml b/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
index 11e4957..7398e7c 100644
--- a/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na WiFi mrežu"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijavite se na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Pristup privatnom DNS serveru nije uspeo"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu vezu"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da biste se ipak povezali"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Uređaj koristi tip mreže <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kada tip mreže <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu. Možda će se naplaćivati troškovi."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznat tip mreže"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Resursi za povezivanje sa sistemom"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Prijavljivanje na WiFi mrežu"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Prijavite se na mrežu"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Dodirnite za opcije"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilna mreža nema pristup internetu"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Mreža nema pristup internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Pristup privatnom DNS serveru nije uspeo"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu vezu"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Dodirnite da biste se ipak povezali"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Uređaj koristi tip mreže <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kada tip mreže <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu. Možda će se naplaćivati troškovi."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilni podaci"</item>
- <item msgid="5520925862115353992">"WiFi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Eternet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobilni podaci"</item>
+ <item msgid="5624324321165953608">"WiFi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Eternet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"nepoznat tip mreže"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-be/strings.xml b/service/ServiceConnectivityResources/res/values-be/strings.xml
index 6b0b1f1..3459cc7 100644
--- a/service/ServiceConnectivityResources/res/values-be/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-be/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Уваход у сетку Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Увайдзіце ў сетку"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> не мае доступу ў інтэрнэт"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Дакраніцеся, каб убачыць параметры"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мабільная сетка не мае доступу ў інтэрнэт"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Сетка не мае доступу ў інтэрнэт"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> мае абмежаваную магчымасць падключэння"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Націсніце, каб падключыцца"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Выкананы пераход да <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Прылада выкарыстоўвае сетку <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, калі ў сетцы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма доступу да інтэрнэту. Можа спаганяцца плата."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Выкананы пераход з <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> да <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"невядомы тып сеткі"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Рэсурсы для падключэння да сістэмы"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Уваход у сетку Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Увайдзіце ў сетку"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> не мае доступу ў інтэрнэт"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Дакраніцеся, каб убачыць параметры"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мабільная сетка не мае доступу ў інтэрнэт"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Сетка не мае доступу ў інтэрнэт"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> мае абмежаваную магчымасць падключэння"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Націсніце, каб падключыцца"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Выкананы пераход да <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Прылада выкарыстоўвае сетку <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, калі ў сетцы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма доступу да інтэрнэту. Можа спаганяцца плата."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Выкананы пераход з <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> да <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мабільная перадача даных"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"мабільная перадача даных"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"невядомы тып сеткі"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-bg/strings.xml b/service/ServiceConnectivityResources/res/values-bg/strings.xml
index 6427bd0..b4ae618 100644
--- a/service/ServiceConnectivityResources/res/values-bg/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-bg/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Влизане в Wi-Fi мрежа"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Вход в мрежата"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> няма достъп до интернет"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Докоснете за опции"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилната мрежа няма достъп до интернет"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежата няма достъп до интернет"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не може да се осъществи достъп до частния DNS сървър"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена свързаност"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Докоснете, за да се свържете въпреки това"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Превключи се към <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Устройството използва <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, когато <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма достъп до интернет. Възможно е да бъдете таксувани."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Превключи се от <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> към <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"неизвестен тип мрежа"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Ресурси за свързаността на системата"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Влизане в Wi-Fi мрежа"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Вход в мрежата"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> няма достъп до интернет"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Докоснете за опции"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мобилната мрежа няма достъп до интернет"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Мрежата няма достъп до интернет"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Не може да се осъществи достъп до частния DNS сървър"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена свързаност"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Докоснете, за да се свържете въпреки това"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Превключи се към <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Устройството използва <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, когато <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма достъп до интернет. Възможно е да бъдете таксувани."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Превключи се от <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> към <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобилни данни"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"виртуална частна мрежа (VPN)"</item>
+ <item msgid="3004933964374161223">"мобилни данни"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"неизвестен тип мрежа"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-bn/strings.xml b/service/ServiceConnectivityResources/res/values-bn/strings.xml
index 74d4cd6..3b32973 100644
--- a/service/ServiceConnectivityResources/res/values-bn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-bn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ওয়াই-ফাই নেটওয়ার্কে সাইন-ইন করুন"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"নেটওয়ার্কে সাইন-ইন করুন"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর ইন্টারনেটে অ্যাক্সেস নেই"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"মোবাইল নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর সীমিত কানেক্টিভিটি আছে"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"তবুও কানেক্ট করতে ট্যাপ করুন"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> এ ইন্টারনেট অ্যাক্সেস না থাকলে <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ব্যবহার করা হয়৷ ডেটা চার্জ প্রযোজ্য৷"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"এই নেটওয়ার্কের প্রকার অজানা"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"সিস্টেম কানেক্টিভিটি রিসোর্সেস"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ওয়াই-ফাই নেটওয়ার্কে সাইন-ইন করুন"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"নেটওয়ার্কে সাইন-ইন করুন"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর ইন্টারনেটে অ্যাক্সেস নেই"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"মোবাইল নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর সীমিত কানেক্টিভিটি আছে"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"তবুও কানেক্ট করতে ট্যাপ করুন"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> এ ইন্টারনেট অ্যাক্সেস না থাকলে <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ব্যবহার করা হয়৷ ডেটা চার্জ প্রযোজ্য৷"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"মোবাইল ডেটা"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ব্লুটুথ"</item>
- <item msgid="1616528372438698248">"ইথারনেট"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"মোবাইল ডেটা"</item>
+ <item msgid="5624324321165953608">"ওয়াই-ফাই"</item>
+ <item msgid="5667906231066981731">"ব্লুটুথ"</item>
+ <item msgid="346574747471703768">"ইথারনেট"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"এই নেটওয়ার্কের ধরন অজানা"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-bs/strings.xml b/service/ServiceConnectivityResources/res/values-bs/strings.xml
index 19f6e1a..0bc0a7c 100644
--- a/service/ServiceConnectivityResources/res/values-bs/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-bs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na WiFi mrežu"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nije moguće pristupiti privatnom DNS serveru"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da se ipak povežete"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prebačeno na: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, uređaj koristi mrežu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata usluge."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Prebačeno iz mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> u <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mrežu"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Izvori povezivosti sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Prijavljivanje na WiFi mrežu"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Prijava na mrežu"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Dodirnite za opcije"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilna mreža nema pristup internetu"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Mreža nema pristup internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Nije moguće pristupiti privatnom DNS serveru"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Dodirnite da se ipak povežete"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Prebačeno na: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, uređaj koristi mrežu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata usluge."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Prebačeno iz mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> u <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mrežu"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"prijenos podataka na mobilnoj mreži"</item>
- <item msgid="5520925862115353992">"WiFi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"prijenos podataka na mobilnoj mreži"</item>
+ <item msgid="5624324321165953608">"WiFi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"nepoznata vrsta mreže"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ca/strings.xml b/service/ServiceConnectivityResources/res/values-ca/strings.xml
index c55684d..22b9dbd 100644
--- a/service/ServiceConnectivityResources/res/values-ca/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ca/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Inicia la sessió a la xarxa Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Inicia la sessió a la xarxa"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no té accés a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca per veure les opcions"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"La xarxa mòbil no té accés a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"La xarxa no té accés a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No es pot accedir al servidor DNS privat"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> té una connectivitat limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca per connectar igualment"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Actualment en ús: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositiu utilitza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> en cas que <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tingui accés a Internet. És possible que s\'hi apliquin càrrecs."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Abans es feia servir la xarxa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>; ara s\'utilitza <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"una tipus de xarxa desconegut"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Recursos de connectivitat del sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Inicia la sessió a la xarxa Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Inicia la sessió a la xarxa"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no té accés a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Toca per veure les opcions"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"La xarxa mòbil no té accés a Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"La xarxa no té accés a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"No es pot accedir al servidor DNS privat"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> té una connectivitat limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Toca per connectar igualment"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Actualment en ús: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"El dispositiu utilitza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> en cas que <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tingui accés a Internet. És possible que s\'hi apliquin càrrecs."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Abans es feia servir la xarxa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>; ara s\'utilitza <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dades mòbils"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"dades mòbils"</item>
+ <item msgid="5624324321165953608">"Wi‑Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"un tipus de xarxa desconegut"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-cs/strings.xml b/service/ServiceConnectivityResources/res/values-cs/strings.xml
index fa8c411..ccf21ee 100644
--- a/service/ServiceConnectivityResources/res/values-cs/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-cs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Přihlásit se k síti Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Přihlásit se k síti"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá přístup k internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Klepnutím zobrazíte možnosti"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilní síť nemá přístup k internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Síť nemá přístup k internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nelze získat přístup k soukromému serveru DNS"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> umožňuje jen omezené připojení"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Klepnutím se i přesto připojíte"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Přechod na síť <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Když síť <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nebude mít přístup k internetu, zařízení použije síť <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Mohou být účtovány poplatky."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Přechod ze sítě <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na síť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznámý typ sítě"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Zdroje pro připojení systému"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Přihlásit se k síti Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Přihlásit se k síti"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá přístup k internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Klepnutím zobrazíte možnosti"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilní síť nemá přístup k internetu"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Síť nemá přístup k internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Nelze získat přístup k soukromému serveru DNS"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> umožňuje jen omezené připojení"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Klepnutím se i přesto připojíte"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Přechod na síť <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Když síť <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nebude mít přístup k internetu, zařízení použije síť <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Mohou být účtovány poplatky."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Přechod ze sítě <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na síť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilní data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobilní data"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"neznámý typ sítě"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-da/strings.xml b/service/ServiceConnectivityResources/res/values-da/strings.xml
index f7be6df..a33143e 100644
--- a/service/ServiceConnectivityResources/res/values-da/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-da/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Log ind på Wi-Fi-netværk"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Log ind på netværk"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetforbindelse"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tryk for at se valgmuligheder"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnetværket har ingen internetadgang"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netværket har ingen internetadgang"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Der er ikke adgang til den private DNS-server"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrænset forbindelse"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tryk for at oprette forbindelse alligevel"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Der blev skiftet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Enheden benytter <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, når der ikke er internetadgang via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Der opkræves muligvis betaling."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Der blev skiftet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en ukendt netværkstype"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Systemets forbindelsesressourcer"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Log ind på Wi-Fi-netværk"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Log ind på netværk"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetforbindelse"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tryk for at se valgmuligheder"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilnetværket har ingen internetadgang"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Netværket har ingen internetadgang"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Der er ikke adgang til den private DNS-server"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrænset forbindelse"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tryk for at oprette forbindelse alligevel"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Der blev skiftet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Enheden benytter <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, når der ikke er internetadgang via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Der opkræves muligvis betaling."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Der blev skiftet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobildata"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobildata"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"en ukendt netværkstype"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-de/strings.xml b/service/ServiceConnectivityResources/res/values-de/strings.xml
index 1e7b80c..96cc7d2 100644
--- a/service/ServiceConnectivityResources/res/values-de/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-de/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"In WLAN anmelden"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Im Netzwerk anmelden"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> hat keinen Internetzugriff"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Für Optionen tippen"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiles Netzwerk hat keinen Internetzugriff"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netzwerk hat keinen Internetzugriff"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Schlechte Verbindung mit <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tippen, um die Verbindung trotzdem herzustellen"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Zu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> gewechselt"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Auf dem Gerät werden <xliff:g id="NEW_NETWORK">%1$s</xliff:g> genutzt, wenn über <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> kein Internet verfügbar ist. Eventuell fallen Gebühren an."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Von \"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>\" zu \"<xliff:g id="NEW_NETWORK">%2$s</xliff:g>\" gewechselt"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ein unbekannter Netzwerktyp"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Systemverbindungsressourcen"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"In WLAN anmelden"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Im Netzwerk anmelden"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> hat keinen Internetzugriff"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Für Optionen tippen"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobiles Netzwerk hat keinen Internetzugriff"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Netzwerk hat keinen Internetzugriff"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Schlechte Verbindung mit <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tippen, um die Verbindung trotzdem herzustellen"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Zu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> gewechselt"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Auf dem Gerät werden <xliff:g id="NEW_NETWORK">%1$s</xliff:g> genutzt, wenn über <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> kein Internet verfügbar ist. Eventuell fallen Gebühren an."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Von \"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>\" zu \"<xliff:g id="NEW_NETWORK">%2$s</xliff:g>\" gewechselt"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"Mobile Daten"</item>
- <item msgid="5520925862115353992">"WLAN"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"Mobile Daten"</item>
+ <item msgid="5624324321165953608">"WLAN"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ein unbekannter Netzwerktyp"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-el/strings.xml b/service/ServiceConnectivityResources/res/values-el/strings.xml
index 89647fd..b5f319d 100644
--- a/service/ServiceConnectivityResources/res/values-el/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-el/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Συνδεθείτε στο δίκτυο Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Σύνδεση στο δίκτυο"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Η εφαρμογή <xliff:g id="NETWORK_SSID">%1$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Πατήστε για να δείτε τις επιλογές"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Το δίκτυο κινητής τηλεφωνίας δεν έχει πρόσβαση στο διαδίκτυο."</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Το δίκτυο δεν έχει πρόσβαση στο διαδίκτυο."</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Το δίκτυο <xliff:g id="NETWORK_SSID">%1$s</xliff:g> έχει περιορισμένη συνδεσιμότητα"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Πατήστε για σύνδεση ούτως ή άλλως"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Μετάβαση σε δίκτυο <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Η συσκευή χρησιμοποιεί το δίκτυο <xliff:g id="NEW_NETWORK">%1$s</xliff:g> όταν το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο. Μπορεί να ισχύουν χρεώσεις."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Μετάβαση από το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> στο δίκτυο <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"άγνωστος τύπος δικτύου"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Πόροι συνδεσιμότητας συστήματος"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Συνδεθείτε στο δίκτυο Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Σύνδεση στο δίκτυο"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Η εφαρμογή <xliff:g id="NETWORK_SSID">%1$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Πατήστε για να δείτε τις επιλογές"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Το δίκτυο κινητής τηλεφωνίας δεν έχει πρόσβαση στο διαδίκτυο."</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Το δίκτυο δεν έχει πρόσβαση στο διαδίκτυο."</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Το δίκτυο <xliff:g id="NETWORK_SSID">%1$s</xliff:g> έχει περιορισμένη συνδεσιμότητα"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Πατήστε για σύνδεση ούτως ή άλλως"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Μετάβαση σε δίκτυο <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Η συσκευή χρησιμοποιεί το δίκτυο <xliff:g id="NEW_NETWORK">%1$s</xliff:g> όταν το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο. Μπορεί να ισχύουν χρεώσεις."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Μετάβαση από το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> στο δίκτυο <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"δεδομένα κινητής τηλεφωνίας"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"δεδομένα κινητής τηλεφωνίας"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"άγνωστος τύπος δικτύου"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml b/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
index d29e2ec..c490cf8 100644
--- a/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System connectivity resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Sign in to a Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobile network has no Internet access"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Network has no Internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobile data"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"an unknown network type"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml b/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
index d29e2ec..c490cf8 100644
--- a/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System connectivity resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Sign in to a Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobile network has no Internet access"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Network has no Internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobile data"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"an unknown network type"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml b/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
index d29e2ec..c490cf8 100644
--- a/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System connectivity resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Sign in to a Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobile network has no Internet access"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Network has no Internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobile data"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"an unknown network type"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml b/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
index d29e2ec..c490cf8 100644
--- a/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System connectivity resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Sign in to a Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobile network has no Internet access"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Network has no Internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobile data"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"an unknown network type"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml b/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
index cd69133..67c3659 100644
--- a/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Sign in to Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobile network has no internet access"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Network has no internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobile data"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"an unknown network type"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml b/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
index 9102dc0..fdca468 100644
--- a/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Accede a una red Wi-Fi."</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Acceder a la red"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>no tiene acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Presiona para ver opciones"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"La red móvil no tiene acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"La red no tiene acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No se puede acceder al servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene conectividad limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Presiona para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Se cambió a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Se cambió de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tipo de red desconocido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Recursos de conectividad del sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Accede a una red Wi-Fi."</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Acceder a la red"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>no tiene acceso a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Presiona para ver opciones"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"La red móvil no tiene acceso a Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"La red no tiene acceso a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"No se puede acceder al servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene conectividad limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Presiona para conectarte de todas formas"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Se cambió a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"El dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Se cambió de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"Datos móviles"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"Datos móviles"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"un tipo de red desconocido"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-es/strings.xml b/service/ServiceConnectivityResources/res/values-es/strings.xml
index 4c15566..f4a7e3d 100644
--- a/service/ServiceConnectivityResources/res/values-es/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-es/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Iniciar sesión en red Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Iniciar sesión en la red"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no tiene acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca para ver opciones"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"La red móvil no tiene acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"La red no tiene acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No se ha podido acceder al servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene una conectividad limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Se ha cambiado a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Se ha cambiado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tipo de red desconocido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Recursos de conectividad del sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Iniciar sesión en red Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Iniciar sesión en la red"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no tiene acceso a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Toca para ver opciones"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"La red móvil no tiene acceso a Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"La red no tiene acceso a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"No se ha podido acceder al servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene una conectividad limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Toca para conectarte de todas formas"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Se ha cambiado a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"El dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Se ha cambiado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"datos móviles"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"datos móviles"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"un tipo de red desconocido"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-et/strings.xml b/service/ServiceConnectivityResources/res/values-et/strings.xml
index 398223a..cf997b3 100644
--- a/service/ServiceConnectivityResources/res/values-et/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-et/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Logi sisse WiFi-võrku"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Võrku sisselogimine"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Võrgul <xliff:g id="NETWORK_SSID">%1$s</xliff:g> puudub Interneti-ühendus"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Puudutage valikute nägemiseks"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiilsidevõrgul puudub Interneti-ühendus"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Võrgul puudub Interneti-ühendus"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Privaatsele DNS-serverile ei pääse juurde"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Võrgu <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ühendus on piiratud"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Puudutage, kui soovite siiski ühenduse luua"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Lülitati võrgule <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Seade kasutab võrku <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, kui võrgul <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> puudub juurdepääs Internetile. Rakenduda võivad tasud."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Lülitati võrgult <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> võrgule <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tundmatu võrgutüüp"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Süsteemi ühenduvuse allikad"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Logi sisse WiFi-võrku"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Võrku sisselogimine"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Võrgul <xliff:g id="NETWORK_SSID">%1$s</xliff:g> puudub Interneti-ühendus"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Puudutage valikute nägemiseks"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobiilsidevõrgul puudub Interneti-ühendus"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Võrgul puudub Interneti-ühendus"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Privaatsele DNS-serverile ei pääse juurde"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Võrgu <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ühendus on piiratud"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Puudutage, kui soovite siiski ühenduse luua"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Lülitati võrgule <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Seade kasutab võrku <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, kui võrgul <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> puudub juurdepääs Internetile. Rakenduda võivad tasud."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Lülitati võrgult <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> võrgule <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiilne andmeside"</item>
- <item msgid="5520925862115353992">"WiFi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobiilne andmeside"</item>
+ <item msgid="5624324321165953608">"WiFi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"tundmatu võrgutüüp"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-eu/strings.xml b/service/ServiceConnectivityResources/res/values-eu/strings.xml
index dd70316..2c4e431 100644
--- a/service/ServiceConnectivityResources/res/values-eu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-eu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Hasi saioa Wi-Fi sarean"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Hasi saioa sarean"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Ezin da konektatu Internetera <xliff:g id="NETWORK_SSID">%1$s</xliff:g> sarearen bidez"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Sakatu aukerak ikusteko"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Sare mugikorra ezin da konektatu Internetera"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Sarea ezin da konektatu Internetera"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ezin da atzitu DNS zerbitzari pribatua"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sareak konektagarritasun murriztua du"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Sakatu hala ere konektatzeko"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Agian kostuak ordaindu beharko dituzu."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"sare mota ezezaguna"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Sistemaren konexio-baliabideak"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Hasi saioa Wi-Fi sarean"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Hasi saioa sarean"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Ezin da konektatu Internetera <xliff:g id="NETWORK_SSID">%1$s</xliff:g> sarearen bidez"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Sakatu aukerak ikusteko"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Sare mugikorra ezin da konektatu Internetera"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Sarea ezin da konektatu Internetera"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Ezin da atzitu DNS zerbitzari pribatua"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sareak konektagarritasun murriztua du"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Sakatu hala ere konektatzeko"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Baliteke zerbait ordaindu behar izatea."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"datu-konexioa"</item>
- <item msgid="5520925862115353992">"Wifia"</item>
- <item msgid="1055487873974272842">"Bluetooth-a"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"datu-konexioa"</item>
+ <item msgid="5624324321165953608">"Wifia"</item>
+ <item msgid="5667906231066981731">"Bluetooth-a"</item>
+ <item msgid="346574747471703768">"Ethernet-a"</item>
+ <item msgid="5734728378097476003">"VPNa"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"sare mota ezezaguna"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-fa/strings.xml b/service/ServiceConnectivityResources/res/values-fa/strings.xml
index 46a946c..296ce8e 100644
--- a/service/ServiceConnectivityResources/res/values-fa/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-fa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ورود به شبکه Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ورود به سیستم شبکه"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> به اینترنت دسترسی ندارد"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"برای گزینهها ضربه بزنید"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"شبکه تلفن همراه به اینترنت دسترسی ندارد"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"شبکه به اینترنت دسترسی ندارد"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"سرور DNS خصوصی قابل دسترسی نیست"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> اتصال محدودی دارد"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"بههرصورت، برای اتصال ضربه بزنید"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"به <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> تغییر کرد"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"وقتی <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> به اینترنت دسترسی نداشته باشد، دستگاه از <xliff:g id="NEW_NETWORK">%1$s</xliff:g> استفاده میکند. ممکن است هزینههایی اعمال شود."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"از <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> به <xliff:g id="NEW_NETWORK">%2$s</xliff:g> تغییر کرد"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نوع شبکه نامشخص"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"منابع اتصال سیستم"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ورود به شبکه Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ورود به سیستم شبکه"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> به اینترنت دسترسی ندارد"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"برای گزینهها ضربه بزنید"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"شبکه تلفن همراه به اینترنت دسترسی ندارد"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"شبکه به اینترنت دسترسی ندارد"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"سرور DNS خصوصی قابل دسترسی نیست"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> اتصال محدودی دارد"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"بههرصورت، برای اتصال ضربه بزنید"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"به <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> تغییر کرد"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"وقتی <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> به اینترنت دسترسی نداشته باشد، دستگاه از <xliff:g id="NEW_NETWORK">%1$s</xliff:g> استفاده میکند. ممکن است هزینههایی اعمال شود."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"از <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> به <xliff:g id="NEW_NETWORK">%2$s</xliff:g> تغییر کرد"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"داده تلفن همراه"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"بلوتوث"</item>
- <item msgid="1616528372438698248">"اترنت"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"داده تلفن همراه"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"بلوتوث"</item>
+ <item msgid="346574747471703768">"اترنت"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"نوع شبکه نامشخص"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-fi/strings.xml b/service/ServiceConnectivityResources/res/values-fi/strings.xml
index dd94441..07d2907 100644
--- a/service/ServiceConnectivityResources/res/values-fi/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-fi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Kirjaudu Wi-Fi-verkkoon"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Kirjaudu verkkoon"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ei ole yhteydessä internetiin"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Näytä vaihtoehdot napauttamalla."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiiliverkko ei ole yhteydessä internetiin"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Verkko ei ole yhteydessä internetiin"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> toimii rajoitetulla yhteydellä"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Yhdistä napauttamalla"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> otettiin käyttöön"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> otetaan käyttöön, kun <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ei voi muodostaa yhteyttä internetiin. Veloitukset ovat mahdollisia."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> poistettiin käytöstä ja <xliff:g id="NEW_NETWORK">%2$s</xliff:g> otettiin käyttöön."</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tuntematon verkon tyyppi"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Järjestelmän yhteysresurssit"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Kirjaudu Wi-Fi-verkkoon"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Kirjaudu verkkoon"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ei ole yhteydessä internetiin"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Näytä vaihtoehdot napauttamalla."</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobiiliverkko ei ole yhteydessä internetiin"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Verkko ei ole yhteydessä internetiin"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> toimii rajoitetulla yhteydellä"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Yhdistä napauttamalla"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> otettiin käyttöön"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> otetaan käyttöön, kun <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ei voi muodostaa yhteyttä internetiin. Veloitukset ovat mahdollisia."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> poistettiin käytöstä ja <xliff:g id="NEW_NETWORK">%2$s</xliff:g> otettiin käyttöön."</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiilidata"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobiilidata"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"tuntematon verkon tyyppi"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml b/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
index 02ef50b..7d5b366 100644
--- a/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Connectez-vous au réseau Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Connectez-vous au réseau"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> n\'offre aucun accès à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Touchez pour afficher les options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Le réseau cellulaire n\'offre aucun accès à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Le réseau n\'offre aucun accès à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Impossible d\'accéder au serveur DNS privé"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> offre une connectivité limitée"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Touchez pour vous connecter quand même"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Passé au réseau <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quand <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas d\'accès à Internet. Des frais peuvent s\'appliquer."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Passé du réseau <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> au <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un type de réseau inconnu"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Ressources de connectivité système"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Connectez-vous au réseau Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Connectez-vous au réseau"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> n\'offre aucun accès à Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Touchez pour afficher les options"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Le réseau cellulaire n\'offre aucun accès à Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Le réseau n\'offre aucun accès à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Impossible d\'accéder au serveur DNS privé"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> offre une connectivité limitée"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Touchez pour vous connecter quand même"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Passé au réseau <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quand <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas d\'accès à Internet. Des frais peuvent s\'appliquer."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Passé du réseau <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> au <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"données cellulaires"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"RPV"</item>
+ <item msgid="3004933964374161223">"données cellulaires"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"RPV"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"un type de réseau inconnu"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-fr/strings.xml b/service/ServiceConnectivityResources/res/values-fr/strings.xml
index 08c9d81..2331d9b 100644
--- a/service/ServiceConnectivityResources/res/values-fr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Connectez-vous au réseau Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Se connecter au réseau"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Aucune connexion à Internet pour <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Appuyez ici pour afficher des options."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Le réseau mobile ne dispose d\'aucun accès à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Le réseau ne dispose d\'aucun accès à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Impossible d\'accéder au serveur DNS privé"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"La connectivité de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> est limitée"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Appuyer pour se connecter quand même"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Nouveau réseau : <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> lorsque <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas de connexion Internet. Des frais peuvent s\'appliquer."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Ancien réseau : <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>. Nouveau réseau : <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"type de réseau inconnu"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Ressources de connectivité système"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Connectez-vous au réseau Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Se connecter au réseau"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Aucune connexion à Internet pour <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Appuyez ici pour afficher des options."</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Le réseau mobile ne dispose d\'aucun accès à Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Le réseau ne dispose d\'aucun accès à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Impossible d\'accéder au serveur DNS privé"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"La connectivité de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> est limitée"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Appuyer pour se connecter quand même"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Nouveau réseau : <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> lorsque <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas de connexion Internet. Des frais peuvent s\'appliquer."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Ancien réseau : <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>. Nouveau réseau : <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"données mobiles"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"données mobiles"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"type de réseau inconnu"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-gl/strings.xml b/service/ServiceConnectivityResources/res/values-gl/strings.xml
index 9f98055..f46f84b 100644
--- a/service/ServiceConnectivityResources/res/values-gl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-gl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Inicia sesión na rede wifi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Inicia sesión na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ten acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca para ver opcións."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A rede de telefonía móbil non ten acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede non ten acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Non se puido acceder ao servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"A conectividade de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> é limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Cambiouse a: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ten acceso a Internet. Pódense aplicar cargos."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Cambiouse de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tipo de rede descoñecido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Recursos de conectividade do sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Inicia sesión na rede wifi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Inicia sesión na rede"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ten acceso a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Toca para ver opcións."</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"A rede de telefonía móbil non ten acceso a Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"A rede non ten acceso a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Non se puido acceder ao servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"A conectividade de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> é limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Toca para conectarte de todas formas"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Cambiouse a: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ten acceso a Internet. Pódense aplicar cargos."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Cambiouse de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"datos móbiles"</item>
- <item msgid="5520925862115353992">"wifi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"datos móbiles"</item>
+ <item msgid="5624324321165953608">"wifi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"un tipo de rede descoñecido"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-gu/strings.xml b/service/ServiceConnectivityResources/res/values-gu/strings.xml
index 4ae5a2c..ec9ecd3 100644
--- a/service/ServiceConnectivityResources/res/values-gu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-gu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"વાઇ-ફાઇ નેટવર્ક પર સાઇન ઇન કરો"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"નેટવર્ક પર સાઇન ઇન કરો"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"વિકલ્પો માટે ટૅપ કરો"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"મોબાઇલ નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> મર્યાદિત કનેક્ટિવિટી ધરાવે છે"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"છતાં કનેક્ટ કરવા માટે ટૅપ કરો"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> પર સ્વિચ કર્યું"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"જ્યારે <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> પાસે કોઈ ઇન્ટરનેટ ઍક્સેસ ન હોય ત્યારે ઉપકરણ <xliff:g id="NEW_NETWORK">%1$s</xliff:g>નો ઉપયોગ કરે છે. શુલ્ક લાગુ થઈ શકે છે."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> પરથી <xliff:g id="NEW_NETWORK">%2$s</xliff:g> પર સ્વિચ કર્યું"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"અજાણ્યો નેટવર્ક પ્રકાર"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"સિસ્ટમની કનેક્ટિવિટીનાં સાધનો"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"વાઇ-ફાઇ નેટવર્ક પર સાઇન ઇન કરો"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"નેટવર્ક પર સાઇન ઇન કરો"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"વિકલ્પો માટે ટૅપ કરો"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"મોબાઇલ નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> મર્યાદિત કનેક્ટિવિટી ધરાવે છે"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"છતાં કનેક્ટ કરવા માટે ટૅપ કરો"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> પર સ્વિચ કર્યું"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"જ્યારે <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> પાસે કોઈ ઇન્ટરનેટ ઍક્સેસ ન હોય ત્યારે ઉપકરણ <xliff:g id="NEW_NETWORK">%1$s</xliff:g>નો ઉપયોગ કરે છે. શુલ્ક લાગુ થઈ શકે છે."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> પરથી <xliff:g id="NEW_NETWORK">%2$s</xliff:g> પર સ્વિચ કર્યું"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"મોબાઇલ ડેટા"</item>
- <item msgid="5520925862115353992">"વાઇ-ફાઇ"</item>
- <item msgid="1055487873974272842">"બ્લૂટૂથ"</item>
- <item msgid="1616528372438698248">"ઇથરનેટ"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"મોબાઇલ ડેટા"</item>
+ <item msgid="5624324321165953608">"વાઇ-ફાઇ"</item>
+ <item msgid="5667906231066981731">"બ્લૂટૂથ"</item>
+ <item msgid="346574747471703768">"ઇથરનેટ"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"કોઈ અજાણ્યો નેટવર્કનો પ્રકાર"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-hi/strings.xml b/service/ServiceConnectivityResources/res/values-hi/strings.xml
index eff1b60..6e3bc6b 100644
--- a/service/ServiceConnectivityResources/res/values-hi/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-hi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"वाई-फ़ाई नेटवर्क में साइन इन करें"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"नेटवर्क में साइन इन करें"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> का इंटरनेट नहीं चल रहा है"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"विकल्पों के लिए टैप करें"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"इस नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> की कनेक्टिविटी सीमित है"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"फिर भी कनेक्ट करने के लिए टैप करें"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> पर ले जाया गया"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> में इंटरनेट की सुविधा नहीं होने पर डिवाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> का इस्तेमाल करता है. इसके लिए शुल्क लिया जा सकता है."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> से <xliff:g id="NEW_NETWORK">%2$s</xliff:g> पर ले जाया गया"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"अज्ञात नेटवर्क प्रकार"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"सिस्टम कनेक्टिविटी के संसाधन"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"वाई-फ़ाई नेटवर्क में साइन इन करें"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"नेटवर्क में साइन इन करें"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> का इंटरनेट नहीं चल रहा है"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"विकल्पों के लिए टैप करें"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"मोबाइल नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"इस नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> की कनेक्टिविटी सीमित है"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"फिर भी कनेक्ट करने के लिए टैप करें"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> पर ले जाया गया"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> में इंटरनेट की सुविधा नहीं होने पर डिवाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> का इस्तेमाल करता है. इसके लिए शुल्क लिया जा सकता है."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> से <xliff:g id="NEW_NETWORK">%2$s</xliff:g> पर ले जाया गया"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
- <item msgid="5520925862115353992">"वाई-फ़ाई"</item>
- <item msgid="1055487873974272842">"ब्लूटूथ"</item>
- <item msgid="1616528372438698248">"ईथरनेट"</item>
- <item msgid="9177085807664964627">"वीपीएन"</item>
+ <item msgid="3004933964374161223">"मोबाइल डेटा"</item>
+ <item msgid="5624324321165953608">"वाई-फ़ाई"</item>
+ <item msgid="5667906231066981731">"ब्लूटूथ"</item>
+ <item msgid="346574747471703768">"ईथरनेट"</item>
+ <item msgid="5734728378097476003">"वीपीएन"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"अज्ञात नेटवर्क टाइप"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-hr/strings.xml b/service/ServiceConnectivityResources/res/values-hr/strings.xml
index 52bfb93..6a6de4c 100644
--- a/service/ServiceConnectivityResources/res/values-hr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-hr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijava na Wi-Fi mrežu"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da biste se ipak povezali"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prelazak na drugu mrežu: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, na uređaju se upotrebljava <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata naknade."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Mreža je promijenjena: <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> > <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Resursi za povezivost sustava"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Prijava na Wi-Fi mrežu"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Prijava na mrežu"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Dodirnite za opcije"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilna mreža nema pristup internetu"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Mreža nema pristup internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Dodirnite da biste se ipak povezali"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Prelazak na drugu mrežu: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, na uređaju se upotrebljava <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata naknade."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Mreža je promijenjena: <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> > <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilni podaci"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobilni podaci"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"nepoznata vrsta mreže"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-hu/strings.xml b/service/ServiceConnectivityResources/res/values-hu/strings.xml
index 2ff59b5..1d39d30 100644
--- a/service/ServiceConnectivityResources/res/values-hu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-hu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Bejelentkezés Wi-Fi hálózatba"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Bejelentkezés a hálózatba"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózaton nincs internet-hozzáférés"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Koppintson a beállítások megjelenítéséhez"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A mobilhálózaton nincs internet-hozzáférés"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A hálózaton nincs internet-hozzáférés"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózat korlátozott kapcsolatot biztosít"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Koppintson, ha mindenképpen csatlakozni szeretne"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Átváltva erre: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> használata, ha nincs internet-hozzáférés <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-kapcsolaton keresztül. A szolgáltató díjat számíthat fel."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Átváltva <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-hálózatról erre: <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ismeretlen hálózati típus"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Rendszerkapcsolat erőforrásai"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Bejelentkezés Wi-Fi hálózatba"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Bejelentkezés a hálózatba"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózaton nincs internet-hozzáférés"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Koppintson a beállítások megjelenítéséhez"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"A mobilhálózaton nincs internet-hozzáférés"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"A hálózaton nincs internet-hozzáférés"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózat korlátozott kapcsolatot biztosít"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Koppintson, ha mindenképpen csatlakozni szeretne"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Átváltva erre: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> használata, ha nincs internet-hozzáférés <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-kapcsolaton keresztül. A szolgáltató díjat számíthat fel."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Átváltva <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-hálózatról erre: <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiladatok"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobiladatok"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ismeretlen hálózati típus"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-hy/strings.xml b/service/ServiceConnectivityResources/res/values-hy/strings.xml
index b35d31c..99386d4 100644
--- a/service/ServiceConnectivityResources/res/values-hy/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-hy/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Մուտք գործեք Wi-Fi ցանց"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Մուտք գործեք ցանց"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցը չունի մուտք ինտերնետին"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Հպեք՝ ընտրանքները տեսնելու համար"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Բջջային ցանցը չի ապահովում ինտերնետ կապ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Ցանցը միացված չէ ինտերնետին"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Մասնավոր DNS սերվերն անհասանելի է"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցի կապը սահմանափակ է"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Հպեք՝ միանալու համար"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Անցել է <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ցանցի"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Երբ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ցանցում ինտերնետ կապ չի լինում, սարքն անցնում է <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ցանցի: Նման դեպքում կարող են վճարներ գանձվել:"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ցանցից անցել է <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ցանցի"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ցանցի անհայտ տեսակ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Մուտք գործեք Wi-Fi ցանց"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Մուտք գործեք ցանց"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցը չունի մուտք ինտերնետին"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Հպեք՝ ընտրանքները տեսնելու համար"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Բջջային ցանցը չի ապահովում ինտերնետ կապ"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Ցանցը միացված չէ ինտերնետին"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Մասնավոր DNS սերվերն անհասանելի է"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցի կապը սահմանափակ է"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Հպեք՝ միանալու համար"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Անցել է <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ցանցի"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Երբ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ցանցում ինտերնետ կապ չի լինում, սարքն անցնում է <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ցանցի: Նման դեպքում կարող են վճարներ գանձվել:"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ցանցից անցել է <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ցանցի"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"բջջային ինտերնետ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"բջջային ինտերնետ"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ցանցի անհայտ տեսակ"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-in/strings.xml b/service/ServiceConnectivityResources/res/values-in/strings.xml
index 27d7d89..f47d257 100644
--- a/service/ServiceConnectivityResources/res/values-in/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-in/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Login ke jaringan Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Login ke jaringan"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tidak memiliki akses internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ketuk untuk melihat opsi"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Jaringan seluler tidak memiliki akses internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Jaringan tidak memiliki akses internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Server DNS pribadi tidak dapat diakses"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> memiliki konektivitas terbatas"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ketuk untuk tetap menyambungkan"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Dialihkan ke <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Perangkat menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> jika <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tidak memiliki akses internet. Tarif mungkin berlaku."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Dialihkan dari <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ke <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"jenis jaringan yang tidak dikenal"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Resource Konektivitas Sistem"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Login ke jaringan Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Login ke jaringan"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tidak memiliki akses internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Ketuk untuk melihat opsi"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Jaringan seluler tidak memiliki akses internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Jaringan tidak memiliki akses internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Server DNS pribadi tidak dapat diakses"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> memiliki konektivitas terbatas"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Ketuk untuk tetap menyambungkan"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Dialihkan ke <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Perangkat menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> jika <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tidak memiliki akses internet. Tarif mungkin berlaku."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Dialihkan dari <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ke <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"data seluler"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"data seluler"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"jenis jaringan yang tidak dikenal"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-is/strings.xml b/service/ServiceConnectivityResources/res/values-is/strings.xml
index 97f42dc..eeba231 100644
--- a/service/ServiceConnectivityResources/res/values-is/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-is/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Skrá inn á Wi-Fi net"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Skrá inn á net"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> er ekki með internetaðgang"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ýttu til að sjá valkosti"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Farsímakerfið er ekki tengt við internetið"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netkerfið er ekki tengt við internetið"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ekki næst í DNS-einkaþjón"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Tengigeta <xliff:g id="NETWORK_SSID">%1$s</xliff:g> er takmörkuð"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ýttu til að tengjast samt"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Skipt yfir á <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Tækið notar <xliff:g id="NEW_NETWORK">%1$s</xliff:g> þegar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> er ekki með internetaðgang. Gjöld kunna að eiga við."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Skipt úr <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> yfir í <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"óþekkt tegund netkerfis"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Tengigögn kerfis"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Skrá inn á Wi-Fi net"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Skrá inn á net"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> er ekki með internetaðgang"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Ýttu til að sjá valkosti"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Farsímakerfið er ekki tengt við internetið"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Netkerfið er ekki tengt við internetið"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Ekki næst í DNS-einkaþjón"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Tengigeta <xliff:g id="NETWORK_SSID">%1$s</xliff:g> er takmörkuð"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Ýttu til að tengjast samt"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Skipt yfir á <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Tækið notar <xliff:g id="NEW_NETWORK">%1$s</xliff:g> þegar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> er ekki með internetaðgang. Gjöld kunna að eiga við."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Skipt úr <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> yfir í <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"farsímagögn"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"farsímagögn"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"óþekkt tegund netkerfis"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-it/strings.xml b/service/ServiceConnectivityResources/res/values-it/strings.xml
index 7836101..ec3ff8c 100644
--- a/service/ServiceConnectivityResources/res/values-it/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-it/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Accedi a rete Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Accedi alla rete"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ha accesso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tocca per le opzioni"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"La rete mobile non ha accesso a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"La rete non ha accesso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Non è possibile accedere al server DNS privato"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ha una connettività limitata"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tocca per connettere comunque"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Passato a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Il dispositivo utilizza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ha accesso a Internet. Potrebbero essere applicati costi."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Passato da <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tipo di rete sconosciuto"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Risorse per connettività di sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Accedi a rete Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Accedi alla rete"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ha accesso a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tocca per le opzioni"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"La rete mobile non ha accesso a Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"La rete non ha accesso a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Non è possibile accedere al server DNS privato"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ha una connettività limitata"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tocca per connettere comunque"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Passato a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Il dispositivo utilizza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ha accesso a Internet. Potrebbero essere applicati costi."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Passato da <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dati mobili"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"dati mobili"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"tipo di rete sconosciuto"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-iw/strings.xml b/service/ServiceConnectivityResources/res/values-iw/strings.xml
index f322b99..d123ebb 100644
--- a/service/ServiceConnectivityResources/res/values-iw/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-iw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"היכנס לרשת Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"היכנס לרשת"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"ל-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> אין גישה לאינטרנט"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"הקש לקבלת אפשרויות"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"לרשת הסלולרית אין גישה לאינטרנט"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"לרשת אין גישה לאינטרנט"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"לא ניתן לגשת לשרת DNS הפרטי"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"הקישוריות של <xliff:g id="NETWORK_SSID">%1$s</xliff:g> מוגבלת"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"כדי להתחבר למרות זאת יש להקיש"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"מעבר אל <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"המכשיר משתמש ברשת <xliff:g id="NEW_NETWORK">%1$s</xliff:g> כשלרשת <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> אין גישה לאינטרנט. עשויים לחול חיובים."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"עבר מרשת <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> לרשת <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"סוג רשת לא מזוהה"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"משאבי קישוריות מערכת"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"היכנס לרשת Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"היכנס לרשת"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"ל-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> אין גישה לאינטרנט"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"הקש לקבלת אפשרויות"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"לרשת הסלולרית אין גישה לאינטרנט"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"לרשת אין גישה לאינטרנט"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"לא ניתן לגשת לשרת DNS הפרטי"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"הקישוריות של <xliff:g id="NETWORK_SSID">%1$s</xliff:g> מוגבלת"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"כדי להתחבר למרות זאת יש להקיש"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"מעבר אל <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"המכשיר משתמש ברשת <xliff:g id="NEW_NETWORK">%1$s</xliff:g> כשלרשת <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> אין גישה לאינטרנט. עשויים לחול חיובים."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"עבר מרשת <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> לרשת <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"חבילת גלישה"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"חבילת גלישה"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"אתרנט"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"סוג רשת לא מזוהה"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ja/strings.xml b/service/ServiceConnectivityResources/res/values-ja/strings.xml
index 1aa3216..7bb6f85 100644
--- a/service/ServiceConnectivityResources/res/values-ja/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ja/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fiネットワークにログイン"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ネットワークにログインしてください"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> はインターネットにアクセスできません"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"タップしてその他のオプションを表示"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"モバイル ネットワークがインターネットに接続されていません"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ネットワークがインターネットに接続されていません"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"プライベート DNS サーバーにアクセスできません"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> の接続が制限されています"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"接続するにはタップしてください"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"「<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>」に切り替えました"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"デバイスで「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」によるインターネット接続ができない場合に「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」を使用します。通信料が発生することがあります。"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"「<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>」から「<xliff:g id="NEW_NETWORK">%2$s</xliff:g>」に切り替えました"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明なネットワーク タイプ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"システム接続リソース"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fiネットワークにログイン"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ネットワークにログインしてください"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> はインターネットにアクセスできません"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"タップしてその他のオプションを表示"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"モバイル ネットワークがインターネットに接続されていません"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"ネットワークがインターネットに接続されていません"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"プライベート DNS サーバーにアクセスできません"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> の接続が制限されています"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"接続するにはタップしてください"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"「<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>」に切り替えました"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"デバイスで「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」によるインターネット接続ができない場合に「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」を使用します。通信料が発生することがあります。"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"「<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>」から「<xliff:g id="NEW_NETWORK">%2$s</xliff:g>」に切り替えました"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"モバイルデータ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"イーサネット"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"モバイルデータ"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"イーサネット"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"不明なネットワーク タイプ"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ka/strings.xml b/service/ServiceConnectivityResources/res/values-ka/strings.xml
index 61d21b5..f42c567 100644
--- a/service/ServiceConnectivityResources/res/values-ka/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ka/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi ქსელთან დაკავშირება"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ქსელში შესვლა"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"შეეხეთ ვარიანტების სანახავად"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"მობილურ ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ის კავშირები შეზღუდულია"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"შეეხეთ, თუ მაინც გსურთ დაკავშირება"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"ახლა გამოიყენება <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"თუ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ინტერნეტთან კავშირს დაკარგავს, მოწყობილობის მიერ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> იქნება გამოყენებული, რამაც შეიძლება დამატებითი ხარჯები გამოიწვიოს."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"ახლა გამოიყენება <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> (გამოიყენებოდა <xliff:g id="NEW_NETWORK">%2$s</xliff:g>)"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"უცნობი ტიპის ქსელი"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"სისტემის კავშირის რესურსები"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi ქსელთან დაკავშირება"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ქსელში შესვლა"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ს არ აქვს ინტერნეტზე წვდომა"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"შეეხეთ ვარიანტების სანახავად"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"მობილურ ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ის კავშირები შეზღუდულია"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"შეეხეთ, თუ მაინც გსურთ დაკავშირება"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"ახლა გამოიყენება <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"თუ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ინტერნეტთან კავშირს დაკარგავს, მოწყობილობის მიერ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> იქნება გამოყენებული, რამაც შეიძლება დამატებითი ხარჯები გამოიწვიოს."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"ახლა გამოიყენება <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> (გამოიყენებოდა <xliff:g id="NEW_NETWORK">%2$s</xliff:g>)"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"მობილური ინტერნეტი"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"მობილური ინტერნეტი"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"უცნობი ტიპის ქსელი"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-kk/strings.xml b/service/ServiceConnectivityResources/res/values-kk/strings.xml
index 969aaef..00c0f39 100644
--- a/service/ServiceConnectivityResources/res/values-kk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-kk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi желісіне кіру"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Желіге кіру"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің интернетті пайдалану мүмкіндігі шектеулі."</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Опциялар үшін түртіңіз"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобильдік желі интернетке қосылмаған."</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Желі интернетке қосылмаған."</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Жеке DNS серверіне кіру мүмкін емес."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің қосылу мүмкіндігі шектеулі."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Бәрібір жалғау үшін түртіңіз."</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> желісіне ауысты"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"желі түрі белгісіз"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Жүйе байланысы ресурстары"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi желісіне кіру"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Желіге кіру"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің интернетті пайдалану мүмкіндігі шектеулі."</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Опциялар үшін түртіңіз"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мобильдік желі интернетке қосылмаған."</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Желі интернетке қосылмаған."</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Жеке DNS серверіне кіру мүмкін емес."</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің қосылу мүмкіндігі шектеулі."</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Бәрібір жалғау үшін түртіңіз."</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> желісіне ауысты"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобильдік деректер"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"мобильдік деректер"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"желі түрі белгісіз"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-km/strings.xml b/service/ServiceConnectivityResources/res/values-km/strings.xml
index da3c337..fa06c5b 100644
--- a/service/ServiceConnectivityResources/res/values-km/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-km/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ចូលបណ្ដាញវ៉ាយហ្វាយ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ចូលទៅបណ្តាញ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ប៉ះសម្រាប់ជម្រើស"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"បណ្ដាញទូរសព្ទចល័តមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"បណ្ដាញមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មានការតភ្ជាប់មានកម្រិត"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"មិនអីទេ ចុចភ្ជាប់ចុះ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"បានប្តូរទៅ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"ឧបករណ៍ប្រើ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> នៅពេលដែល <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិត។ អាចគិតថ្លៃលើការប្រើប្រាស់ទិន្នន័យ។"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"បានប្តូរពី <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ទៅ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ប្រភេទបណ្តាញមិនស្គាល់"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ធនធានតភ្ជាប់ប្រព័ន្ធ"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ចូលបណ្ដាញវ៉ាយហ្វាយ"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ចូលទៅបណ្តាញ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"ប៉ះសម្រាប់ជម្រើស"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"បណ្ដាញទូរសព្ទចល័តមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"បណ្ដាញមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មានការតភ្ជាប់មានកម្រិត"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"មិនអីទេ ចុចភ្ជាប់ចុះ"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"បានប្តូរទៅ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"ឧបករណ៍ប្រើ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> នៅពេលដែល <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិត។ អាចគិតថ្លៃលើការប្រើប្រាស់ទិន្នន័យ។"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"បានប្តូរពី <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ទៅ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ទិន្នន័យទូរសព្ទចល័ត"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ប៊្លូធូស"</item>
- <item msgid="1616528372438698248">"អ៊ីសឺរណិត"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"ទិន្នន័យទូរសព្ទចល័ត"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"ប៊្លូធូស"</item>
+ <item msgid="346574747471703768">"អ៊ីសឺរណិត"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ប្រភេទបណ្តាញដែលមិនស្គាល់"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-kn/strings.xml b/service/ServiceConnectivityResources/res/values-kn/strings.xml
index 3b5e047..cde8fac 100644
--- a/service/ServiceConnectivityResources/res/values-kn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-kn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ಮೊಬೈಲ್ ನೆಟ್ವರ್ಕ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ನೆಟ್ವರ್ಕ್ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಸೀಮಿತ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿದೆ"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ಹೇಗಾದರೂ ಸಂಪರ್ಕಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ ಹೊಂದಿಲ್ಲದಿರುವಾಗ, ಸಾಧನವು <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ಬಳಸುತ್ತದೆ. ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ರಿಂದ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ಅಪರಿಚಿತ ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ಸಿಸ್ಟಂ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆ ಮಾಹಿತಿಯ ಮೂಲಗಳು"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"ಮೊಬೈಲ್ ನೆಟ್ವರ್ಕ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"ನೆಟ್ವರ್ಕ್ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಸೀಮಿತ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿದೆ"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"ಹೇಗಾದರೂ ಸಂಪರ್ಕಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ ಹೊಂದಿಲ್ಲದಿರುವಾಗ, ಸಾಧನವು <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ಬಳಸುತ್ತದೆ. ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ರಿಂದ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ಮೊಬೈಲ್ ಡೇಟಾ"</item>
- <item msgid="5520925862115353992">"ವೈ-ಫೈ"</item>
- <item msgid="1055487873974272842">"ಬ್ಲೂಟೂತ್"</item>
- <item msgid="1616528372438698248">"ಇಥರ್ನೆಟ್"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"ಮೊಬೈಲ್ ಡೇಟಾ"</item>
+ <item msgid="5624324321165953608">"ವೈ-ಫೈ"</item>
+ <item msgid="5667906231066981731">"ಬ್ಲೂಟೂತ್"</item>
+ <item msgid="346574747471703768">"ಇಥರ್ನೆಟ್"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ಅಪರಿಚಿತ ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರ"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ko/strings.xml b/service/ServiceConnectivityResources/res/values-ko/strings.xml
index 874bd75..eef59a9 100644
--- a/service/ServiceConnectivityResources/res/values-ko/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ko/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi 네트워크에 로그인"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"네트워크에 로그인"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>이(가) 인터넷에 액세스할 수 없습니다."</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"탭하여 옵션 보기"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"모바일 네트워크에 인터넷이 연결되어 있지 않습니다."</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"네트워크에 인터넷이 연결되어 있지 않습니다."</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>에서 연결을 제한했습니다."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"계속 연결하려면 탭하세요."</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>(으)로 전환"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>(으)로 인터넷에 연결할 수 없는 경우 기기에서 <xliff:g id="NEW_NETWORK">%1$s</xliff:g>이(가) 사용됩니다. 요금이 부과될 수 있습니다."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>에서 <xliff:g id="NEW_NETWORK">%2$s</xliff:g>(으)로 전환"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"알 수 없는 네트워크 유형"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"시스템 연결 리소스"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi 네트워크에 로그인"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"네트워크에 로그인"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>이(가) 인터넷에 액세스할 수 없습니다."</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"탭하여 옵션 보기"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"모바일 네트워크에 인터넷이 연결되어 있지 않습니다."</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"네트워크에 인터넷이 연결되어 있지 않습니다."</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>에서 연결을 제한했습니다."</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"계속 연결하려면 탭하세요."</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>(으)로 전환"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>(으)로 인터넷에 연결할 수 없는 경우 기기에서 <xliff:g id="NEW_NETWORK">%1$s</xliff:g>이(가) 사용됩니다. 요금이 부과될 수 있습니다."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>에서 <xliff:g id="NEW_NETWORK">%2$s</xliff:g>(으)로 전환"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"모바일 데이터"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"블루투스"</item>
- <item msgid="1616528372438698248">"이더넷"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"모바일 데이터"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"블루투스"</item>
+ <item msgid="346574747471703768">"이더넷"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"알 수 없는 네트워크 유형"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ky/strings.xml b/service/ServiceConnectivityResources/res/values-ky/strings.xml
index 1ace4dc..0027c8a 100644
--- a/service/ServiceConnectivityResources/res/values-ky/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ky/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi түйүнүнө кирүү"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Тармакка кирүү"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилдик Интернет жок"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Тармактын Интернет жок"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Жеке DNS сервери жеткиликсиз"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> байланышы чектелген"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Баары бир туташуу үчүн таптаңыз"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> тармагына которуштурулду"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> тармагы Интернетке туташпай турганда, түзмөгүңүз <xliff:g id="NEW_NETWORK">%1$s</xliff:g> тармагын колдонот. Акы алынышы мүмкүн."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> дегенден <xliff:g id="NEW_NETWORK">%2$s</xliff:g> тармагына которуштурулду"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"белгисиз тармак түрү"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Системанын байланыш булагы"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi түйүнүнө кирүү"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Тармакка кирүү"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мобилдик Интернет жок"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Тармактын Интернет жок"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Жеке DNS сервери жеткиликсиз"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> байланышы чектелген"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Баары бир туташуу үчүн таптаңыз"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> тармагына которуштурулду"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> тармагы Интернетке туташпай турганда, түзмөгүңүз <xliff:g id="NEW_NETWORK">%1$s</xliff:g> тармагын колдонот. Акы алынышы мүмкүн."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> дегенден <xliff:g id="NEW_NETWORK">%2$s</xliff:g> тармагына которуштурулду"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобилдик трафик"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"мобилдик трафик"</item>
+ <item msgid="5624324321165953608">"Wi‑Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"белгисиз тармак түрү"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-lo/strings.xml b/service/ServiceConnectivityResources/res/values-lo/strings.xml
index 3db497e..64419f9 100644
--- a/service/ServiceConnectivityResources/res/values-lo/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-lo/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ເຂົ້າສູ່ລະບົບເຄືອຂ່າຍ Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ເຄືອຂ່າຍມືຖືບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ເຄືອຂ່າຍບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ມີການເຊື່ອມຕໍ່ທີ່ຈຳກັດ"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ແຕະເພື່ອຢືນຢັນການເຊື່ອມຕໍ່"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"ສະຫຼັບໄປໃຊ້ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ແລ້ວ"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"ອຸປະກອນຈະໃຊ້ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ເມື່ອ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ. ອາດມີການຮຽກເກັບຄ່າບໍລິການ."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"ສະຫຼັບຈາກ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ໄປໃຊ້ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ແລ້ວ"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ບໍ່ຮູ້ຈັກປະເພດເຄືອຂ່າຍ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ແຫຼ່ງຂໍ້ມູນການເຊື່ອມຕໍ່ລະບົບ"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ເຂົ້າສູ່ລະບົບເຄືອຂ່າຍ Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"ເຄືອຂ່າຍມືຖືບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"ເຄືອຂ່າຍບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ມີການເຊື່ອມຕໍ່ທີ່ຈຳກັດ"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"ແຕະເພື່ອຢືນຢັນການເຊື່ອມຕໍ່"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"ສະຫຼັບໄປໃຊ້ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ແລ້ວ"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"ອຸປະກອນຈະໃຊ້ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ເມື່ອ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ. ອາດມີການຮຽກເກັບຄ່າບໍລິການ."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"ສະຫຼັບຈາກ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ໄປໃຊ້ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ແລ້ວ"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ອິນເຕີເນັດມືຖື"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"ອີເທີເນັດ"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"ອິນເຕີເນັດມືຖື"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"ອີເທີເນັດ"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ບໍ່ຮູ້ຈັກປະເພດເຄືອຂ່າຍ"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-lt/strings.xml b/service/ServiceConnectivityResources/res/values-lt/strings.xml
index c639c1f..f73f142 100644
--- a/service/ServiceConnectivityResources/res/values-lt/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-lt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prisijungti prie „Wi-Fi“ tinklo"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prisijungti prie tinklo"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ negali pasiekti interneto"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Palieskite, kad būtų rodomos parinktys."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiliojo ryšio tinkle nėra prieigos prie interneto"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Tinkle nėra prieigos prie interneto"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Privataus DNS serverio negalima pasiekti"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ ryšys apribotas"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Palieskite, jei vis tiek norite prisijungti"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Perjungta į tinklą <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Įrenginyje naudojamas kitas tinklas (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>), kai dabartiniame tinkle (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nėra interneto ryšio. Gali būti taikomi mokesčiai."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Perjungta iš tinklo <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> į tinklą <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nežinomas tinklo tipas"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Prisijungti prie „Wi-Fi“ tinklo"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Prisijungti prie tinklo"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ negali pasiekti interneto"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Palieskite, kad būtų rodomos parinktys."</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobiliojo ryšio tinkle nėra prieigos prie interneto"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Tinkle nėra prieigos prie interneto"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Privataus DNS serverio negalima pasiekti"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ ryšys apribotas"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Palieskite, jei vis tiek norite prisijungti"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Perjungta į tinklą <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Įrenginyje naudojamas kitas tinklas (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>), kai dabartiniame tinkle (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nėra interneto ryšio. Gali būti taikomi mokesčiai."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Perjungta iš tinklo <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> į tinklą <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiliojo ryšio duomenys"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Eternetas"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobiliojo ryšio duomenys"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Eternetas"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"nežinomas tinklo tipas"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-lv/strings.xml b/service/ServiceConnectivityResources/res/values-lv/strings.xml
index 5774603..9d26c40 100644
--- a/service/ServiceConnectivityResources/res/values-lv/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-lv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Pierakstieties Wi-Fi tīklā"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Pierakstīšanās tīklā"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nav piekļuves internetam"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Pieskarieties, lai skatītu iespējas."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilajā tīklā nav piekļuves internetam."</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Tīklā nav piekļuves internetam."</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nevar piekļūt privātam DNS serverim."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ir ierobežota savienojamība"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Lai tik un tā izveidotu savienojumu, pieskarieties"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Pārslēdzās uz tīklu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kad vienā tīklā (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nav piekļuves internetam, ierīcē tiek izmantots cits tīkls (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>). Var tikt piemērota maksa."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Pārslēdzās no tīkla <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> uz tīklu <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nezināms tīkla veids"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Sistēmas savienojamības resursi"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Pierakstieties Wi-Fi tīklā"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Pierakstīšanās tīklā"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nav piekļuves internetam"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Pieskarieties, lai skatītu iespējas."</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilajā tīklā nav piekļuves internetam."</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Tīklā nav piekļuves internetam."</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Nevar piekļūt privātam DNS serverim."</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ir ierobežota savienojamība"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Lai tik un tā izveidotu savienojumu, pieskarieties"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Pārslēdzās uz tīklu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Kad vienā tīklā (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nav piekļuves internetam, ierīcē tiek izmantots cits tīkls (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>). Var tikt piemērota maksa."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Pārslēdzās no tīkla <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> uz tīklu <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilie dati"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobilie dati"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"nezināms tīkla veids"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-mk/strings.xml b/service/ServiceConnectivityResources/res/values-mk/strings.xml
index 053c7cf..fb105e0 100644
--- a/service/ServiceConnectivityResources/res/values-mk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-mk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Најавете се на мрежа на Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Најавете се на мрежа"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема интернет-пристап"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Допрете за опции"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилната мрежа нема интернет-пристап"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежата нема интернет-пристап"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не може да се пристапи до приватниот DNS-сервер"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена поврзливост"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Допрете за да се поврзете и покрај тоа"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Префрлено на <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Уредот користи <xliff:g id="NEW_NETWORK">%1$s</xliff:g> кога <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема пристап до интернет. Може да се наплатат трошоци."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Префрлено од <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"непознат тип мрежа"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Најавете се на мрежа на Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Најавете се на мрежа"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема интернет-пристап"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Допрете за опции"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мобилната мрежа нема интернет-пристап"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Мрежата нема интернет-пристап"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Не може да се пристапи до приватниот DNS-сервер"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена поврзливост"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Допрете за да се поврзете и покрај тоа"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Префрлено на <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Уредот користи <xliff:g id="NEW_NETWORK">%1$s</xliff:g> кога <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема пристап до интернет. Може да се наплатат трошоци."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Префрлено од <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобилен интернет"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Етернет"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"мобилен интернет"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Етернет"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"непознат тип мрежа"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ml/strings.xml b/service/ServiceConnectivityResources/res/values-ml/strings.xml
index 3e80cf1..9a51238 100644
--- a/service/ServiceConnectivityResources/res/values-ml/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ml/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"വൈഫൈ നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"മൊബെെൽ നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് പരിമിതമായ കണക്റ്റിവിറ്റി ഉണ്ട്"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ഏതുവിധേനയും കണക്റ്റ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> എന്നതിലേക്ക് മാറി"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-ന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ലാത്തപ്പോൾ ഉപകരണം <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ഉപയോഗിക്കുന്നു. നിരക്കുകൾ ബാധകമായേക്കാം."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> നെറ്റ്വർക്കിൽ നിന്ന് <xliff:g id="NEW_NETWORK">%2$s</xliff:g> നെറ്റ്വർക്കിലേക്ക് മാറി"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"തിരിച്ചറിയാനാകാത്ത ഒരു നെറ്റ്വർക്ക് തരം"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"സിസ്റ്റം കണക്റ്റിവിറ്റി ഉറവിടങ്ങൾ"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"വൈഫൈ നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"മൊബെെൽ നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് പരിമിതമായ കണക്റ്റിവിറ്റി ഉണ്ട്"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"ഏതുവിധേനയും കണക്റ്റ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> എന്നതിലേക്ക് മാറി"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-ന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ലാത്തപ്പോൾ ഉപകരണം <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ഉപയോഗിക്കുന്നു. നിരക്കുകൾ ബാധകമായേക്കാം."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> നെറ്റ്വർക്കിൽ നിന്ന് <xliff:g id="NEW_NETWORK">%2$s</xliff:g> നെറ്റ്വർക്കിലേക്ക് മാറി"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"മൊബൈൽ ഡാറ്റ"</item>
- <item msgid="5520925862115353992">"വൈഫൈ"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"ഇതര്നെറ്റ്"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"മൊബൈൽ ഡാറ്റ"</item>
+ <item msgid="5624324321165953608">"വൈഫൈ"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"ഇതർനെറ്റ്"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"അജ്ഞാതമായ നെറ്റ്വർക്ക് തരം"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-mn/strings.xml b/service/ServiceConnectivityResources/res/values-mn/strings.xml
index 214fc0c..8372533 100644
--- a/service/ServiceConnectivityResources/res/values-mn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-mn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi сүлжээнд нэвтэрнэ үү"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Сүлжээнд нэвтэрнэ үү"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Сонголт хийхийн тулд товшино уу"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Сүлжээнд интернэт хандалт байхгүй байна"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Хувийн DNS серверт хандах боломжгүй байна"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ямар ч тохиолдолд холбогдохын тулд товших"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> руу шилжүүлсэн"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> интернет холболтгүй үед төхөөрөмж <xliff:g id="NEW_NETWORK">%1$s</xliff:g>-г ашигладаг. Төлбөр гарч болзошгүй."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-с <xliff:g id="NEW_NETWORK">%2$s</xliff:g> руу шилжүүлсэн"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"сүлжээний тодорхойгүй төрөл"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Системийн холболтын нөөцүүд"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi сүлжээнд нэвтэрнэ үү"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Сүлжээнд нэвтэрнэ үү"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Сонголт хийхийн тулд товшино уу"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Сүлжээнд интернэт хандалт байхгүй байна"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Хувийн DNS серверт хандах боломжгүй байна"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Ямар ч тохиолдолд холбогдохын тулд товших"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> руу шилжүүлсэн"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> интернет холболтгүй үед төхөөрөмж <xliff:g id="NEW_NETWORK">%1$s</xliff:g>-г ашигладаг. Төлбөр гарч болзошгүй."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-с <xliff:g id="NEW_NETWORK">%2$s</xliff:g> руу шилжүүлсэн"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобайл дата"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Этернэт"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"мобайл дата"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Этернэт"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"үл мэдэгдэх сүлжээний төрөл"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-mr/strings.xml b/service/ServiceConnectivityResources/res/values-mr/strings.xml
index c4b1998..658b19b 100644
--- a/service/ServiceConnectivityResources/res/values-mr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-mr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"वाय-फाय नेटवर्कमध्ये साइन इन करा"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"नेटवर्कवर साइन इन करा"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला इंटरनेट अॅक्सेस नाही"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"पर्यायांसाठी टॅप करा"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला मर्यादित कनेक्टिव्हिटी आहे"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"तरीही कनेक्ट करण्यासाठी टॅप करा"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> वर स्विच केले"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> कडे इंटरनेटचा अॅक्सेस नसताना डिव्हाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> वापरते. शुल्क लागू शकते."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> वरून <xliff:g id="NEW_NETWORK">%2$s</xliff:g> वर स्विच केले"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"अज्ञात नेटवर्क प्रकार"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"सिस्टम कनेक्टिव्हिटी चे स्रोत"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"वाय-फाय नेटवर्कमध्ये साइन इन करा"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"नेटवर्कवर साइन इन करा"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला इंटरनेट अॅक्सेस नाही"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"पर्यायांसाठी टॅप करा"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"मोबाइल नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला मर्यादित कनेक्टिव्हिटी आहे"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"तरीही कनेक्ट करण्यासाठी टॅप करा"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> वर स्विच केले"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> कडे इंटरनेटचा अॅक्सेस नसताना डिव्हाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> वापरते. शुल्क लागू शकते."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> वरून <xliff:g id="NEW_NETWORK">%2$s</xliff:g> वर स्विच केले"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
- <item msgid="5520925862115353992">"वाय-फाय"</item>
- <item msgid="1055487873974272842">"ब्लूटूथ"</item>
- <item msgid="1616528372438698248">"इथरनेट"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"मोबाइल डेटा"</item>
+ <item msgid="5624324321165953608">"वाय-फाय"</item>
+ <item msgid="5667906231066981731">"ब्लूटूथ"</item>
+ <item msgid="346574747471703768">"इथरनेट"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"अज्ञात नेटवर्क प्रकार"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ms/strings.xml b/service/ServiceConnectivityResources/res/values-ms/strings.xml
index 529d0bd..84b242c 100644
--- a/service/ServiceConnectivityResources/res/values-ms/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ms/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Log masuk ke rangkaian Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Log masuk ke rangkaian"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiada akses Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ketik untuk mendapatkan pilihan"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Rangkaian mudah alih tiada akses Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Rangkaian tiada akses Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Pelayan DNS peribadi tidak boleh diakses"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> mempunyai kesambungan terhad"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ketik untuk menyambung juga"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Beralih kepada <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Peranti menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> apabila <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tiada akses Internet. Bayaran mungkin dikenakan."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Beralih daripada <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kepada <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"jenis rangkaian tidak diketahui"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Sumber Kesambungan Sistem"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Log masuk ke rangkaian Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Log masuk ke rangkaian"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiada akses Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Ketik untuk mendapatkan pilihan"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Rangkaian mudah alih tiada akses Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Rangkaian tiada akses Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Pelayan DNS peribadi tidak boleh diakses"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> mempunyai kesambungan terhad"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Ketik untuk menyambung juga"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Beralih kepada <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Peranti menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> apabila <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tiada akses Internet. Bayaran mungkin dikenakan."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Beralih daripada <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kepada <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"data mudah alih"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"data mudah alih"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"jenis rangkaian tidak diketahui"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-my/strings.xml b/service/ServiceConnectivityResources/res/values-my/strings.xml
index 50590fa..6832263 100644
--- a/service/ServiceConnectivityResources/res/values-my/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-my/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ဝိုင်ဖိုင်ကွန်ရက်သို့ လက်မှတ်ထိုးဝင်ပါ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"မိုဘိုင်းကွန်ရက်တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ကွန်ရက်တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် ချိတ်ဆက်မှုကို ကန့်သတ်ထားသည်"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"မည်သို့ပင်ဖြစ်စေ ချိတ်ဆက်ရန် တို့ပါ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ဖြင့် အင်တာနက် အသုံးမပြုနိုင်သည့်အချိန်တွင် စက်ပစ္စည်းသည် <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ကို သုံးပါသည်။ ဒေတာသုံးစွဲခ ကျသင့်နိုင်ပါသည်။"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> မှ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"အမည်မသိကွန်ရက်အမျိုးအစား"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"စနစ်ချိတ်ဆက်နိုင်မှု ရင်းမြစ်များ"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ဝိုင်ဖိုင်ကွန်ရက်သို့ လက်မှတ်ထိုးဝင်ပါ"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"မိုဘိုင်းကွန်ရက်တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"ကွန်ရက်တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် ချိတ်ဆက်မှုကို ကန့်သတ်ထားသည်"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"မည်သို့ပင်ဖြစ်စေ ချိတ်ဆက်ရန် တို့ပါ"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ဖြင့် အင်တာနက် အသုံးမပြုနိုင်သည့်အချိန်တွင် စက်ပစ္စည်းသည် <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ကို သုံးပါသည်။ ဒေတာသုံးစွဲခ ကျသင့်နိုင်ပါသည်။"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> မှ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"မိုဘိုင်းဒေတာ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ဘလူးတုသ်"</item>
- <item msgid="1616528372438698248">"အီသာနက်"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"မိုဘိုင်းဒေတာ"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"ဘလူးတုသ်"</item>
+ <item msgid="346574747471703768">"အီသာနက်"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"အမည်မသိကွန်ရက်အမျိုးအစား"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-nb/strings.xml b/service/ServiceConnectivityResources/res/values-nb/strings.xml
index b89d198..00a0728 100644
--- a/service/ServiceConnectivityResources/res/values-nb/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Logg på Wi-Fi-nettverket"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Logg på nettverk"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internettilkobling"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Trykk for å få alternativer"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnettverket har ingen internettilgang"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Nettverket har ingen internettilgang"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Den private DNS-tjeneren kan ikke nås"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrenset tilkobling"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Trykk for å koble til likevel"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Byttet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Enheten bruker <xliff:g id="NEW_NETWORK">%1$s</xliff:g> når <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ikke har Internett-tilgang. Avgifter kan påløpe."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Byttet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en ukjent nettverkstype"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Ressurser for systemtilkobling"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Logg på Wi-Fi-nettverket"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Logg på nettverk"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internettilkobling"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Trykk for å få alternativer"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilnettverket har ingen internettilgang"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Nettverket har ingen internettilgang"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Den private DNS-tjeneren kan ikke nås"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrenset tilkobling"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Trykk for å koble til likevel"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Byttet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Enheten bruker <xliff:g id="NEW_NETWORK">%1$s</xliff:g> når <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ikke har Internett-tilgang. Avgifter kan påløpe."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Byttet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobildata"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobildata"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"en ukjent nettverkstype"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ne/strings.xml b/service/ServiceConnectivityResources/res/values-ne/strings.xml
index bdcfa3b..2eaf162 100644
--- a/service/ServiceConnectivityResources/res/values-ne/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ne/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi नेटवर्कमा साइन इन गर्नुहोस्"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"सञ्जालमा साइन इन गर्नुहोस्"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को जडान सीमित छ"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"जसरी भए पनि जडान गर्न ट्याप गर्नुहोस्"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> मा बदल्नुहोस्"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> मार्फत इन्टरनेटमाथि पहुँच राख्न नसकेको अवस्थामा यन्त्रले <xliff:g id="NEW_NETWORK">%1$s</xliff:g> प्रयोग गर्दछ। शुल्क लाग्न सक्छ।"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> बाट <xliff:g id="NEW_NETWORK">%2$s</xliff:g> मा परिवर्तन गरियो"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"नेटवर्कको कुनै अज्ञात प्रकार"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"सिस्टम कनेक्टिभिटीका स्रोतहरू"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi नेटवर्कमा साइन इन गर्नुहोस्"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"सञ्जालमा साइन इन गर्नुहोस्"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को इन्टरनेटमाथि पहुँच छैन"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"मोबाइल नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को जडान सीमित छ"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"जसरी भए पनि जडान गर्न ट्याप गर्नुहोस्"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> मा बदल्नुहोस्"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> मार्फत इन्टरनेटमाथि पहुँच राख्न नसकेको अवस्थामा यन्त्रले <xliff:g id="NEW_NETWORK">%1$s</xliff:g> प्रयोग गर्दछ। शुल्क लाग्न सक्छ।"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> बाट <xliff:g id="NEW_NETWORK">%2$s</xliff:g> मा परिवर्तन गरियो"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ब्लुटुथ"</item>
- <item msgid="1616528372438698248">"इथरनेट"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"मोबाइल डेटा"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"ब्लुटुथ"</item>
+ <item msgid="346574747471703768">"इथरनेट"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"नेटवर्कको कुनै अज्ञात प्रकार"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-nl/strings.xml b/service/ServiceConnectivityResources/res/values-nl/strings.xml
index 8ecff6e..394c552 100644
--- a/service/ServiceConnectivityResources/res/values-nl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-nl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Inloggen bij wifi-netwerk"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Inloggen bij netwerk"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft geen internettoegang"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tik voor opties"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiel netwerk heeft geen internettoegang"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netwerk heeft geen internettoegang"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Geen toegang tot privé-DNS-server"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft beperkte connectiviteit"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tik om toch verbinding te maken"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Overgeschakeld naar <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Apparaat gebruikt <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internetverbinding heeft. Er kunnen kosten in rekening worden gebracht."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Overgeschakeld van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> naar <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"een onbekend netwerktype"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Resources voor systeemconnectiviteit"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Inloggen bij wifi-netwerk"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Inloggen bij netwerk"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft geen internettoegang"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tik voor opties"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobiel netwerk heeft geen internettoegang"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Netwerk heeft geen internettoegang"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Geen toegang tot privé-DNS-server"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft beperkte connectiviteit"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tik om toch verbinding te maken"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Overgeschakeld naar <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Apparaat gebruikt <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internetverbinding heeft. Er kunnen kosten in rekening worden gebracht."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Overgeschakeld van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> naar <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiele data"</item>
- <item msgid="5520925862115353992">"Wifi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobiele data"</item>
+ <item msgid="5624324321165953608">"Wifi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"een onbekend netwerktype"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-or/strings.xml b/service/ServiceConnectivityResources/res/values-or/strings.xml
index 6ec1f9d..8b85884 100644
--- a/service/ServiceConnectivityResources/res/values-or/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-or/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ମୋବାଇଲ୍ ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ସୀମିତ ସଂଯୋଗ ଅଛି"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ତଥାପି ଯୋଗାଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ର ଇଣ୍ଟରନେଟ୍ ଆକ୍ସେସ୍ ନଥିବାବେଳେ ଡିଭାଇସ୍ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ବ୍ୟବହାର କରିଥାଏ। ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ।"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ରୁ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ଏକ ଅଜଣା ନେଟ୍ୱର୍କ ପ୍ରକାର"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ସିଷ୍ଟମର ସଂଯୋଗ ସମ୍ବନ୍ଧିତ ରିସୋର୍ସଗୁଡ଼ିକ"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"ମୋବାଇଲ୍ ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ସୀମିତ ସଂଯୋଗ ଅଛି"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"ତଥାପି ଯୋଗାଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ର ଇଣ୍ଟରନେଟ୍ ଆକ୍ସେସ୍ ନଥିବାବେଳେ ଡିଭାଇସ୍ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ବ୍ୟବହାର କରିଥାଏ। ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ।"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ରୁ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ମୋବାଇଲ୍ ଡାଟା"</item>
- <item msgid="5520925862115353992">"ୱାଇ-ଫାଇ"</item>
- <item msgid="1055487873974272842">"ବ୍ଲୁଟୁଥ"</item>
- <item msgid="1616528372438698248">"ଇଥରନେଟ୍"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"ମୋବାଇଲ ଡାଟା"</item>
+ <item msgid="5624324321165953608">"ୱାଇ-ଫାଇ"</item>
+ <item msgid="5667906231066981731">"ବ୍ଲୁଟୁଥ୍"</item>
+ <item msgid="346574747471703768">"ଇଥରନେଟ୍"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ଏକ ଅଜଣା ନେଟୱାର୍କ ପ୍ରକାର"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-pa/strings.xml b/service/ServiceConnectivityResources/res/values-pa/strings.xml
index e948193..9f71cac 100644
--- a/service/ServiceConnectivityResources/res/values-pa/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਸੀਮਤ ਕਨੈਕਟੀਵਿਟੀ ਹੈ"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ਫਿਰ ਵੀ ਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"ਬਦਲਕੇ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ਲਿਆਂਦਾ ਗਿਆ"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ਦੀ ਇੰਟਰਨੈੱਟ \'ਤੇ ਪਹੁੰਚ ਨਾ ਹੋਣ \'ਤੇ ਡੀਵਾਈਸ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ। ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ।"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ਤੋਂ ਬਦਲਕੇ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> \'ਤੇ ਕੀਤਾ ਗਿਆ"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ਇੱਕ ਅਗਿਆਤ ਨੈੱਟਵਰਕ ਕਿਸਮ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ਸਿਸਟਮ ਕਨੈਕਟੀਵਿਟੀ ਸਰੋਤ"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਸੀਮਤ ਕਨੈਕਟੀਵਿਟੀ ਹੈ"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"ਫਿਰ ਵੀ ਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"ਬਦਲਕੇ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ਲਿਆਂਦਾ ਗਿਆ"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ਦੀ ਇੰਟਰਨੈੱਟ \'ਤੇ ਪਹੁੰਚ ਨਾ ਹੋਣ \'ਤੇ ਡੀਵਾਈਸ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ। ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ।"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ਤੋਂ ਬਦਲਕੇ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> \'ਤੇ ਕੀਤਾ ਗਿਆ"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ਮੋਬਾਈਲ ਡਾਟਾ"</item>
- <item msgid="5520925862115353992">"ਵਾਈ-ਫਾਈ"</item>
- <item msgid="1055487873974272842">"ਬਲੂਟੁੱਥ"</item>
- <item msgid="1616528372438698248">"ਈਥਰਨੈੱਟ"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"ਮੋਬਾਈਲ ਡਾਟਾ"</item>
+ <item msgid="5624324321165953608">"ਵਾਈ-ਫਾਈ"</item>
+ <item msgid="5667906231066981731">"ਬਲੂਟੁੱਥ"</item>
+ <item msgid="346574747471703768">"ਈਥਰਨੈੱਟ"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ਕੋਈ ਅਗਿਆਤ ਨੈੱਟਵਰਕ ਦੀ ਕਿਸਮ"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-pl/strings.xml b/service/ServiceConnectivityResources/res/values-pl/strings.xml
index 038328f..cc84e29 100644
--- a/service/ServiceConnectivityResources/res/values-pl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Zaloguj się w sieci Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Zaloguj się do sieci"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nie ma dostępu do internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Kliknij, by wyświetlić opcje"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Sieć komórkowa nie ma dostępu do internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Sieć nie ma dostępu do internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Brak dostępu do prywatnego serwera DNS"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ma ograniczoną łączność"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Kliknij, by mimo to nawiązać połączenie"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Zmieniono na połączenie typu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Urządzenie korzysta z połączenia typu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, gdy <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nie dostępu do internetu. Mogą zostać naliczone opłaty."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Przełączono z połączenia typu <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>."</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nieznany typ sieci"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Zasoby systemowe dotyczące łączności"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Zaloguj się w sieci Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Zaloguj się do sieci"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nie ma dostępu do internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Kliknij, by wyświetlić opcje"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Sieć komórkowa nie ma dostępu do internetu"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Sieć nie ma dostępu do internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Brak dostępu do prywatnego serwera DNS"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ma ograniczoną łączność"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Kliknij, by mimo to nawiązać połączenie"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Zmieniono na połączenie typu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Urządzenie korzysta z połączenia typu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, gdy <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nie dostępu do internetu. Mogą zostać naliczone opłaty."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Przełączono z połączenia typu <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>."</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilna transmisja danych"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobilna transmisja danych"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"nieznany typ sieci"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml b/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
index fe37405..3c15a76 100644
--- a/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Fazer login na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Fazer login na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para ver opções"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível acessar o servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para conectar mesmo assim"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Recursos de conectividade do sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Fazer login na rede Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Fazer login na rede"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Toque para ver opções"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"A rede móvel não tem acesso à Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"A rede não tem acesso à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Não é possível acessar o servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Toque para conectar mesmo assim"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dados móveis"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"dados móveis"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"um tipo de rede desconhecido"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml b/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
index 69060f7..48dde75 100644
--- a/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Iniciar sessão na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Início de sessão na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para obter mais opções"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível aceder ao servidor DNS."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conetividade limitada."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para ligar mesmo assim."</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Mudou para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Podem aplicar-se custos."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Mudou de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Recursos de conetividade do sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Iniciar sessão na rede Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Início de sessão na rede"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Toque para obter mais opções"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"A rede móvel não tem acesso à Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"A rede não tem acesso à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Não é possível aceder ao servidor DNS."</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conetividade limitada."</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Toque para ligar mesmo assim."</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Mudou para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Podem aplicar-se custos."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Mudou de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dados móveis"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"dados móveis"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"um tipo de rede desconhecido"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-pt/strings.xml b/service/ServiceConnectivityResources/res/values-pt/strings.xml
index fe37405..3c15a76 100644
--- a/service/ServiceConnectivityResources/res/values-pt/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Fazer login na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Fazer login na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para ver opções"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível acessar o servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para conectar mesmo assim"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Recursos de conectividade do sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Fazer login na rede Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Fazer login na rede"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Toque para ver opções"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"A rede móvel não tem acesso à Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"A rede não tem acesso à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Não é possível acessar o servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Toque para conectar mesmo assim"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dados móveis"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"dados móveis"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"um tipo de rede desconhecido"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ro/strings.xml b/service/ServiceConnectivityResources/res/values-ro/strings.xml
index 227b7be..fa5848f 100644
--- a/service/ServiceConnectivityResources/res/values-ro/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ro/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Conectați-vă la rețeaua Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Conectați-vă la rețea"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Atingeți pentru opțiuni"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Rețeaua mobilă nu are acces la internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Rețeaua nu are acces la internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Serverul DNS privat nu poate fi accesat"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Atingeți pentru a vă conecta oricum"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tip de rețea necunoscut"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Resurse pentru conectivitatea sistemului"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Conectați-vă la rețeaua Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Conectați-vă la rețea"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Atingeți pentru opțiuni"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Rețeaua mobilă nu are acces la internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Rețeaua nu are acces la internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Serverul DNS privat nu poate fi accesat"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Atingeți pentru a vă conecta oricum"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"date mobile"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"date mobile"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"un tip de rețea necunoscut"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ru/strings.xml b/service/ServiceConnectivityResources/res/values-ru/strings.xml
index 18acf81..2e074ed 100644
--- a/service/ServiceConnectivityResources/res/values-ru/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Подключение к Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Регистрация в сети"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Сеть \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" не подключена к Интернету"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Нажмите, чтобы показать варианты."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобильная сеть не подключена к Интернету"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Сеть не подключена к Интернету"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Доступа к частному DNS-серверу нет."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Подключение к сети \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" ограничено"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Нажмите, чтобы подключиться"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Новое подключение: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Устройство использует <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, если подключение к сети <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> недоступно. Может взиматься плата за передачу данных."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"неизвестный тип сети"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Подключение к Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Регистрация в сети"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Сеть \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" не подключена к Интернету"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Нажмите, чтобы показать варианты."</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мобильная сеть не подключена к Интернету"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Сеть не подключена к Интернету"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Доступа к частному DNS-серверу нет."</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Подключение к сети \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" ограничено"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Нажмите, чтобы подключиться"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Новое подключение: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Устройство использует <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, если подключение к сети <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> недоступно. Может взиматься плата за передачу данных."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобильный Интернет"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"мобильный интернет"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"неизвестный тип сети"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-si/strings.xml b/service/ServiceConnectivityResources/res/values-si/strings.xml
index 6307c2b..a4f720a 100644
--- a/service/ServiceConnectivityResources/res/values-si/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-si/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi ජාලයට පුරනය වන්න"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ජාලයට පුරනය වන්න"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"විකල්ප සඳහා තට්ටු කරන්න"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ජංගම ජාලවලට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ජාලයට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට සීමිත සබැඳුම් හැකියාවක් ඇත"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"කෙසේ වෙතත් ඉදිරියට යාමට තට්ටු කරන්න"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> වෙත මාරු විය"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"උපාංගය <xliff:g id="NEW_NETWORK">%1$s</xliff:g> <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> සඳහා අන්තර්ජාල ප්රවේශය නැති විට භාවිත කරයි. ගාස්තු අදාළ විය හැකිය."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> සිට <xliff:g id="NEW_NETWORK">%2$s</xliff:g> වෙත මාරු විය"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"නොදන්නා ජාල වර්ගයකි"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"පද්ධති සබැඳුම් හැකියා සම්පත්"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi ජාලයට පුරනය වන්න"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ජාලයට පුරනය වන්න"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට අන්තර්ජාල ප්රවේශය නැත"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"විකල්ප සඳහා තට්ටු කරන්න"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"ජංගම ජාලවලට අන්තර්ජාල ප්රවේශය නැත"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"ජාලයට අන්තර්ජාල ප්රවේශය නැත"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට සීමිත සබැඳුම් හැකියාවක් ඇත"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"කෙසේ වෙතත් ඉදිරියට යාමට තට්ටු කරන්න"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> වෙත මාරු විය"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"උපාංගය <xliff:g id="NEW_NETWORK">%1$s</xliff:g> <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> සඳහා අන්තර්ජාල ප්රවේශය නැති විට භාවිත කරයි. ගාස්තු අදාළ විය හැකිය."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> සිට <xliff:g id="NEW_NETWORK">%2$s</xliff:g> වෙත මාරු විය"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ජංගම දත්ත"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"බ්ලූටූත්"</item>
- <item msgid="1616528372438698248">"ඊතර්නෙට්"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"ජංගම දත්ත"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"බ්ලූටූත්"</item>
+ <item msgid="346574747471703768">"ඊතර්නෙට්"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"නොදන්නා ජාල වර්ගයකි"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-sk/strings.xml b/service/ServiceConnectivityResources/res/values-sk/strings.xml
index e894fef..432b670 100644
--- a/service/ServiceConnectivityResources/res/values-sk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prihlásiť sa do siete Wi‑Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prihlásenie do siete"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá prístup k internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Klepnutím získate možnosti"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilná sieť nemá prístup k internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Sieť nemá prístup k internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> má obmedzené pripojenie"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ak sa chcete aj napriek tomu pripojiť, klepnite"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prepnuté na sieť: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Keď <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nemá prístup k internetu, zariadenie používa <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Môžu sa účtovať poplatky."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Prepnuté zo siete <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sieť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznámy typ siete"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Zdroje možností pripojenia systému"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Prihlásiť sa do siete Wi‑Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Prihlásenie do siete"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá prístup k internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Klepnutím získate možnosti"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilná sieť nemá prístup k internetu"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Sieť nemá prístup k internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> má obmedzené pripojenie"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Ak sa chcete aj napriek tomu pripojiť, klepnite"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Prepnuté na sieť: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Keď <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nemá prístup k internetu, zariadenie používa <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Môžu sa účtovať poplatky."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Prepnuté zo siete <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sieť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilné dáta"</item>
- <item msgid="5520925862115353992">"Wi‑Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobilné dáta"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"neznámy typ siete"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-sl/strings.xml b/service/ServiceConnectivityResources/res/values-sl/strings.xml
index 954b324..b727614 100644
--- a/service/ServiceConnectivityResources/res/values-sl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavite se v omrežje Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava v omrežje"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Omrežje <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nima dostopa do interneta"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dotaknite se za možnosti"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilno omrežje nima dostopa do interneta"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Omrežje nima dostopa do interneta"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Povezljivost omrežja <xliff:g id="NETWORK_SSID">%1$s</xliff:g> je omejena"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dotaknite se, da kljub temu vzpostavite povezavo"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Preklopljeno na omrežje vrste <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Naprava uporabi omrežje vrste <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, ko omrežje vrste <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nima dostopa do interneta. Prenos podatkov se lahko zaračuna."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Preklopljeno z omrežja vrste <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na omrežje vrste <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznana vrsta omrežja"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Viri povezljivosti sistema"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Prijavite se v omrežje Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Prijava v omrežje"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Omrežje <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nima dostopa do interneta"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Dotaknite se za možnosti"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilno omrežje nima dostopa do interneta"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Omrežje nima dostopa do interneta"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Povezljivost omrežja <xliff:g id="NETWORK_SSID">%1$s</xliff:g> je omejena"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Dotaknite se, da kljub temu vzpostavite povezavo"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Preklopljeno na omrežje vrste <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Naprava uporabi omrežje vrste <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, ko omrežje vrste <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nima dostopa do interneta. Prenos podatkov se lahko zaračuna."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Preklopljeno z omrežja vrste <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na omrežje vrste <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"prenos podatkov v mobilnem omrežju"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"prenos podatkov v mobilnem omrežju"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"neznana vrsta omrežja"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-sq/strings.xml b/service/ServiceConnectivityResources/res/values-sq/strings.xml
index bd5d052..385c75c 100644
--- a/service/ServiceConnectivityResources/res/values-sq/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sq/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Identifikohu në rrjetin Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Identifikohu në rrjet"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nuk ka qasje në internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Trokit për opsionet"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Rrjeti celular nuk ka qasje në internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Rrjeti nuk ka qasje në internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Serveri privat DNS nuk mund të qaset"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ka lidhshmëri të kufizuar"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Trokit për t\'u lidhur gjithsesi"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Kaloi te <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Pajisja përdor <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kur <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nuk ka qasje në internet. Mund të zbatohen tarifa."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Kaloi nga <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> te <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"një lloj rrjeti i panjohur"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Burimet e lidhshmërisë së sistemit"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Identifikohu në rrjetin Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Identifikohu në rrjet"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nuk ka qasje në internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Trokit për opsionet"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Rrjeti celular nuk ka qasje në internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Rrjeti nuk ka qasje në internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Serveri privat DNS nuk mund të qaset"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ka lidhshmëri të kufizuar"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Trokit për t\'u lidhur gjithsesi"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Kaloi te <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Pajisja përdor <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kur <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nuk ka qasje në internet. Mund të zbatohen tarifa."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Kaloi nga <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> te <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"të dhënat celulare"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Eternet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"të dhënat celulare"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Eternet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"një lloj rrjeti i panjohur"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-sr/strings.xml b/service/ServiceConnectivityResources/res/values-sr/strings.xml
index 8bedbff..928dc79 100644
--- a/service/ServiceConnectivityResources/res/values-sr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Пријављивање на WiFi мрежу"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Пријавите се на мрежу"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема приступ интернету"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Додирните за опције"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилна мрежа нема приступ интернету"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежа нема приступ интернету"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Приступ приватном DNS серверу није успео"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничену везу"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Додирните да бисте се ипак повезали"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Прешли сте на тип мреже <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Уређај користи тип мреже <xliff:g id="NEW_NETWORK">%1$s</xliff:g> када тип мреже <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема приступ интернету. Можда ће се наплаћивати трошкови."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Прешли сте са типа мреже <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на тип мреже <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"непознат тип мреже"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Ресурси за повезивање са системом"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Пријављивање на WiFi мрежу"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Пријавите се на мрежу"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема приступ интернету"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Додирните за опције"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мобилна мрежа нема приступ интернету"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Мрежа нема приступ интернету"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Приступ приватном DNS серверу није успео"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничену везу"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Додирните да бисте се ипак повезали"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Прешли сте на тип мреже <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Уређај користи тип мреже <xliff:g id="NEW_NETWORK">%1$s</xliff:g> када тип мреже <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема приступ интернету. Можда ће се наплаћивати трошкови."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Прешли сте са типа мреже <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на тип мреже <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобилни подаци"</item>
- <item msgid="5520925862115353992">"WiFi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Етернет"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"мобилни подаци"</item>
+ <item msgid="5624324321165953608">"WiFi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Етернет"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"непознат тип мреже"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-sv/strings.xml b/service/ServiceConnectivityResources/res/values-sv/strings.xml
index b3f1763..d714124 100644
--- a/service/ServiceConnectivityResources/res/values-sv/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Logga in på ett Wi-Fi-nätverk"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Logga in på nätverket"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetanslutning"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tryck för alternativ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnätverket har ingen internetanslutning"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Nätverket har ingen internetanslutning"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Det går inte att komma åt den privata DNS-servern."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begränsad anslutning"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tryck för att ansluta ändå"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Byte av nätverk till <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> används på enheten när det inte finns internetåtkomst via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Avgifter kan tillkomma."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Byte av nätverk från <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> till <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en okänd nätverkstyp"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Resurser för systemanslutning"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Logga in på ett wifi-nätverk"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Logga in på nätverket"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetanslutning"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tryck för alternativ"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobilnätverket har ingen internetanslutning"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Nätverket har ingen internetanslutning"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Det går inte att komma åt den privata DNS-servern."</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begränsad anslutning"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tryck för att ansluta ändå"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Byte av nätverk till <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> används på enheten när det inte finns internetåtkomst via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Avgifter kan tillkomma."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Byte av nätverk från <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> till <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobildata"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobildata"</item>
+ <item msgid="5624324321165953608">"Wifi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"en okänd nätverkstyp"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-sw/strings.xml b/service/ServiceConnectivityResources/res/values-sw/strings.xml
index 9674654..15d6cab 100644
--- a/service/ServiceConnectivityResources/res/values-sw/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Ingia kwa mtandao wa Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Ingia katika mtandao"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> haina uwezo wa kufikia intaneti"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Gusa ili upate chaguo"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mtandao wa simu hauna uwezo wa kufikia intaneti"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mtandao hauna uwezo wa kufikia intaneti"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ina muunganisho unaofikia huduma chache."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Gusa ili uunganishe tu"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina intaneti. Huenda ukalipishwa."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"aina ya mtandao isiyojulikana"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Nyenzo za Muunganisho wa Mfumo"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Ingia kwa mtandao wa Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Ingia katika mtandao"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> haina uwezo wa kufikia intaneti"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Gusa ili upate chaguo"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mtandao wa simu hauna uwezo wa kufikia intaneti"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Mtandao hauna uwezo wa kufikia intaneti"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ina muunganisho unaofikia huduma chache."</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Gusa ili uunganishe tu"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina intaneti. Huenda ukalipishwa."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"data ya simu"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethaneti"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"data ya mtandao wa simu"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethaneti"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"aina ya mtandao isiyojulikana"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ta/strings.xml b/service/ServiceConnectivityResources/res/values-ta/strings.xml
index 12604cb..9850a35 100644
--- a/service/ServiceConnectivityResources/res/values-ta/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ta/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"வைஃபை நெட்வொர்க்கில் உள்நுழையவும்"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"நெட்வொர்க்கில் உள்நுழையவும்"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"விருப்பங்களுக்கு, தட்டவும்"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"மொபைல் நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> வரம்பிற்கு உட்பட்ட இணைப்புநிலையைக் கொண்டுள்ளது"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"எப்படியேனும் இணைப்பதற்குத் தட்டவும்"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> நெட்வொர்க்கில் இண்டர்நெட் அணுகல் இல்லாததால், சாதனமானது <xliff:g id="NEW_NETWORK">%1$s</xliff:g> நெட்வொர்க்கைப் பயன்படுத்துகிறது. கட்டணங்கள் விதிக்கப்படலாம்."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> இலிருந்து <xliff:g id="NEW_NETWORK">%2$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"தெரியாத நெட்வொர்க் வகை"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"சிஸ்டம் இணைப்பு தகவல்கள்"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"வைஃபை நெட்வொர்க்கில் உள்நுழையவும்"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"நெட்வொர்க்கில் உள்நுழையவும்"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"விருப்பங்களுக்கு, தட்டவும்"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"மொபைல் நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> வரம்பிற்கு உட்பட்ட இணைப்புநிலையைக் கொண்டுள்ளது"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"எப்படியேனும் இணைப்பதற்குத் தட்டவும்"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> நெட்வொர்க்கில் இண்டர்நெட் அணுகல் இல்லாததால், சாதனமானது <xliff:g id="NEW_NETWORK">%1$s</xliff:g> நெட்வொர்க்கைப் பயன்படுத்துகிறது. கட்டணங்கள் விதிக்கப்படலாம்."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> இலிருந்து <xliff:g id="NEW_NETWORK">%2$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"மொபைல் டேட்டா"</item>
- <item msgid="5520925862115353992">"வைஃபை"</item>
- <item msgid="1055487873974272842">"புளூடூத்"</item>
- <item msgid="1616528372438698248">"ஈத்தர்நெட்"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"மொபைல் டேட்டா"</item>
+ <item msgid="5624324321165953608">"வைஃபை"</item>
+ <item msgid="5667906231066981731">"புளூடூத்"</item>
+ <item msgid="346574747471703768">"ஈதர்நெட்"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"தெரியாத நெட்வொர்க் வகை"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-te/strings.xml b/service/ServiceConnectivityResources/res/values-te/strings.xml
index 84a8640..f7182a8 100644
--- a/service/ServiceConnectivityResources/res/values-te/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-te/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ఎంపికల కోసం నొక్కండి"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"మొబైల్ నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> పరిమిత కనెక్టివిటీని కలిగి ఉంది"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ఏదేమైనా కనెక్ట్ చేయడానికి నొక్కండి"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>కి మార్చబడింది"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"పరికరం <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేనప్పుడు <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ని ఉపయోగిస్తుంది. ఛార్జీలు వర్తించవచ్చు."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> నుండి <xliff:g id="NEW_NETWORK">%2$s</xliff:g>కి మార్చబడింది"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"తెలియని నెట్వర్క్ రకం"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"సిస్టమ్ కనెక్టివిటీ రిసోర్స్లు"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"ఎంపికల కోసం నొక్కండి"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"మొబైల్ నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> పరిమిత కనెక్టివిటీని కలిగి ఉంది"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"ఏదేమైనా కనెక్ట్ చేయడానికి నొక్కండి"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>కి మార్చబడింది"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"పరికరం <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేనప్పుడు <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ని ఉపయోగిస్తుంది. ఛార్జీలు వర్తించవచ్చు."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> నుండి <xliff:g id="NEW_NETWORK">%2$s</xliff:g>కి మార్చబడింది"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"మొబైల్ డేటా"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"బ్లూటూత్"</item>
- <item msgid="1616528372438698248">"ఈథర్నెట్"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"మొబైల్ డేటా"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"బ్లూటూత్"</item>
+ <item msgid="346574747471703768">"ఈథర్నెట్"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"తెలియని నెట్వర్క్ రకం"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-th/strings.xml b/service/ServiceConnectivityResources/res/values-th/strings.xml
index 1616e51..7049309 100644
--- a/service/ServiceConnectivityResources/res/values-th/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-th/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ลงชื่อเข้าใช้เครือข่าย WiFi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ลงชื่อเข้าใช้เครือข่าย"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"แตะเพื่อดูตัวเลือก"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"เครือข่ายมือถือไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"เครือข่ายไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> มีการเชื่อมต่อจำกัด"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"แตะเพื่อเชื่อมต่อ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"เปลี่ยนเป็น <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"อุปกรณ์จะใช้ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> เมื่อ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้ โดยอาจมีค่าบริการ"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"เปลี่ยนจาก <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> เป็น <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ประเภทเครือข่ายที่ไม่รู้จัก"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"ทรัพยากรการเชื่อมต่อของระบบ"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"ลงชื่อเข้าใช้เครือข่าย WiFi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"ลงชื่อเข้าใช้เครือข่าย"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"แตะเพื่อดูตัวเลือก"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"เครือข่ายมือถือไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"เครือข่ายไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> มีการเชื่อมต่อจำกัด"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"แตะเพื่อเชื่อมต่อ"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"เปลี่ยนเป็น <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"อุปกรณ์จะใช้ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> เมื่อ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้ โดยอาจมีค่าบริการ"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"เปลี่ยนจาก <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> เป็น <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"เน็ตมือถือ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"บลูทูธ"</item>
- <item msgid="1616528372438698248">"อีเทอร์เน็ต"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"อินเทอร์เน็ตมือถือ"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"บลูทูธ"</item>
+ <item msgid="346574747471703768">"อีเทอร์เน็ต"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"ประเภทเครือข่ายที่ไม่รู้จัก"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-tl/strings.xml b/service/ServiceConnectivityResources/res/values-tl/strings.xml
index 3bf1ce4..c866fd4 100644
--- a/service/ServiceConnectivityResources/res/values-tl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-tl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Mag-sign in sa Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Mag-sign in sa network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Walang access sa internet ang <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"I-tap para sa mga opsyon"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Walang access sa internet ang mobile network"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Walang access sa internet ang network"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Hindi ma-access ang pribadong DNS server"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Limitado ang koneksyon ng <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"I-tap para kumonekta pa rin"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Lumipat sa <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Ginagamit ng device ang <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kapag walang access sa internet ang <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Maaaring may mga malapat na singilin."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Lumipat sa <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mula sa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"isang hindi kilalang uri ng network"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Mga Resource ng Pagkakonekta ng System"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Mag-sign in sa Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Mag-sign in sa network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Walang access sa internet ang <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"I-tap para sa mga opsyon"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Walang access sa internet ang mobile network"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Walang access sa internet ang network"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Hindi ma-access ang pribadong DNS server"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Limitado ang koneksyon ng <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"I-tap para kumonekta pa rin"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Lumipat sa <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Ginagamit ng device ang <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kapag walang access sa internet ang <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Maaaring may mga malapat na singilin."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Lumipat sa <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mula sa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobile data"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"isang hindi kilalang uri ng network"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-tr/strings.xml b/service/ServiceConnectivityResources/res/values-tr/strings.xml
index 5c326e5..c4930a8 100644
--- a/service/ServiceConnectivityResources/res/values-tr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-tr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Kablosuz ağda oturum açın"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Ağda oturum açın"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ağının internet bağlantısı yok"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Seçenekler için dokunun"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil ağın internet bağlantısı yok"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Ağın internet bağlantısı yok"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Gizli DNS sunucusuna erişilemiyor"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sınırlı bağlantıya sahip"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Yine de bağlanmak için dokunun"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ağına geçildi"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ağının internet erişimi olmadığında cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ağını kullanır. Bunun için ödeme alınabilir."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ağından <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ağına geçildi"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"bilinmeyen ağ türü"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Sistem Bağlantı Kaynakları"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Kablosuz ağda oturum açın"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Ağda oturum açın"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ağının internet bağlantısı yok"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Seçenekler için dokunun"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobil ağın internet bağlantısı yok"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Ağın internet bağlantısı yok"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Gizli DNS sunucusuna erişilemiyor"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sınırlı bağlantıya sahip"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Yine de bağlanmak için dokunun"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ağına geçildi"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ağının internet erişimi olmadığında cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ağını kullanır. Bunun için ödeme alınabilir."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ağından <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ağına geçildi"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobil veri"</item>
- <item msgid="5520925862115353992">"Kablosuz"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobil veri"</item>
+ <item msgid="5624324321165953608">"Kablosuz"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"bilinmeyen ağ türü"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-uk/strings.xml b/service/ServiceConnectivityResources/res/values-uk/strings.xml
index d1382da..8811263 100644
--- a/service/ServiceConnectivityResources/res/values-uk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-uk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Вхід у мережу Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Вхід у мережу"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Мережа <xliff:g id="NETWORK_SSID">%1$s</xliff:g> не має доступу до Інтернету"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Торкніться, щоб відкрити опції"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобільна мережа не має доступу до Інтернету"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Мережа не має доступу до Інтернету"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Немає доступу до приватного DNS-сервера"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Підключення до мережі <xliff:g id="NETWORK_SSID">%1$s</xliff:g> обмежено"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Натисніть, щоб усе одно підключитися"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Пристрій перейшов на мережу <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Коли мережа <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> не має доступу до Інтернету, використовується <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Може стягуватися плата."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Пристрій перейшов з мережі <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на мережу <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"невідомий тип мережі"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Ресурси для підключення системи"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Вхід у мережу Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Вхід у мережу"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"Мережа <xliff:g id="NETWORK_SSID">%1$s</xliff:g> не має доступу до Інтернету"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Торкніться, щоб відкрити опції"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Мобільна мережа не має доступу до Інтернету"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Мережа не має доступу до Інтернету"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Немає доступу до приватного DNS-сервера"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"Підключення до мережі <xliff:g id="NETWORK_SSID">%1$s</xliff:g> обмежено"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Натисніть, щоб усе одно підключитися"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Пристрій перейшов на мережу <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Коли мережа <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> не має доступу до Інтернету, використовується <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Може стягуватися плата."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Пристрій перейшов з мережі <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на мережу <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобільне передавання даних"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"Мережа VPN"</item>
+ <item msgid="3004933964374161223">"мобільний Інтернет"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"невідомий тип мережі"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-ur/strings.xml b/service/ServiceConnectivityResources/res/values-ur/strings.xml
index 3c031ad..8f9656c 100644
--- a/service/ServiceConnectivityResources/res/values-ur/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ur/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi نیٹ ورک میں سائن ان کریں"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"نیٹ ورک میں سائن ان کریں"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"اختیارات کیلئے تھپتھپائیں"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"موبائل نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کی کنیکٹوٹی محدود ہے"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"بہر حال منسلک کرنے کے لیے تھپتھپائیں"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> پر سوئچ ہو گیا"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"جب <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> کو انٹرنیٹ تک رسائی نہیں ہوتی ہے تو آلہ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> کا استعمال کرتا ہے۔ چارجز لاگو ہو سکتے ہیں۔"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> سے <xliff:g id="NEW_NETWORK">%2$s</xliff:g> پر سوئچ ہو گیا"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نیٹ ورک کی نامعلوم قسم"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"سسٹم کنیکٹوٹی کے وسائل"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi نیٹ ورک میں سائن ان کریں"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"نیٹ ورک میں سائن ان کریں"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"اختیارات کیلئے تھپتھپائیں"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"موبائل نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کی کنیکٹوٹی محدود ہے"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"بہر حال منسلک کرنے کے لیے تھپتھپائیں"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> پر سوئچ ہو گیا"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"جب <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> کو انٹرنیٹ تک رسائی نہیں ہوتی ہے تو آلہ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> کا استعمال کرتا ہے۔ چارجز لاگو ہو سکتے ہیں۔"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> سے <xliff:g id="NEW_NETWORK">%2$s</xliff:g> پر سوئچ ہو گیا"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"موبائل ڈیٹا"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"بلوٹوتھ"</item>
- <item msgid="1616528372438698248">"ایتھرنیٹ"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"موبائل ڈیٹا"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"بلوٹوتھ"</item>
+ <item msgid="346574747471703768">"ایتھرنیٹ"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"نامعلوم نیٹ ورک کی قسم"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-uz/strings.xml b/service/ServiceConnectivityResources/res/values-uz/strings.xml
index 7518db3..d7285ad 100644
--- a/service/ServiceConnectivityResources/res/values-uz/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-uz/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi tarmoqqa kirish"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Tarmoqqa kirish"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda internetga ruxsati yoʻq"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Variantlarni ko‘rsatish uchun bosing"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil tarmoq internetga ulanmagan"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Tarmoq internetga ulanmagan"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Xususiy DNS server ishlamayapti"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda aloqa cheklangan"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Baribir ulash uchun bosing"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Yangi ulanish: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Agar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tarmoqda internet uzilsa, qurilma <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ga ulanadi. Sarflangan trafik uchun haq olinishi mumkin."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> tarmog‘idan <xliff:g id="NEW_NETWORK">%2$s</xliff:g> tarmog‘iga o‘tildi"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"noma’lum tarmoq turi"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Tizim aloqa resurslari"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Wi-Fi tarmoqqa kirish"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Tarmoqqa kirish"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda internetga ruxsati yoʻq"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Variantlarni ko‘rsatish uchun bosing"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mobil tarmoq internetga ulanmagan"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Tarmoq internetga ulanmagan"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Xususiy DNS server ishlamayapti"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda aloqa cheklangan"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Baribir ulash uchun bosing"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Yangi ulanish: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Agar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tarmoqda internet uzilsa, qurilma <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ga ulanadi. Sarflangan trafik uchun haq olinishi mumkin."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> tarmog‘idan <xliff:g id="NEW_NETWORK">%2$s</xliff:g> tarmog‘iga o‘tildi"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobil internet"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"mobil internet"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"nomaʼlum tarmoq turi"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-vi/strings.xml b/service/ServiceConnectivityResources/res/values-vi/strings.xml
index d2284d4..239fb81 100644
--- a/service/ServiceConnectivityResources/res/values-vi/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-vi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Đăng nhập vào mạng Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Đăng nhập vào mạng"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> không có quyền truy cập Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Nhấn để biết tùy chọn"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mạng di động không có quyền truy cập Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mạng không có quyền truy cập Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Không thể truy cập máy chủ DNS riêng tư"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> có khả năng kết nối giới hạn"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Nhấn để tiếp tục kết nối"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Đã chuyển sang <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Thiết bị sử dụng <xliff:g id="NEW_NETWORK">%1$s</xliff:g> khi <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> không có quyền truy cập Internet. Bạn có thể phải trả phí."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Đã chuyển từ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> sang <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"loại mạng không xác định"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Tài nguyên kết nối hệ thống"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Đăng nhập vào mạng Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Đăng nhập vào mạng"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> không có quyền truy cập Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Nhấn để biết tùy chọn"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Mạng di động không có quyền truy cập Internet"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Mạng không có quyền truy cập Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Không thể truy cập máy chủ DNS riêng tư"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> có khả năng kết nối giới hạn"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Nhấn để tiếp tục kết nối"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Đã chuyển sang <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Thiết bị sử dụng <xliff:g id="NEW_NETWORK">%1$s</xliff:g> khi <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> không có quyền truy cập Internet. Bạn có thể phải trả phí."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Đã chuyển từ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> sang <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dữ liệu di động"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"dữ liệu di động"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"Bluetooth"</item>
+ <item msgid="346574747471703768">"Ethernet"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"loại mạng không xác định"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml b/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
index 813482b..e318c0b 100644
--- a/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"登录到WLAN网络"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"登录到网络"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 无法访问互联网"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"点按即可查看相关选项"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"此移动网络无法访问互联网"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"此网络无法访问互联网"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"无法访问私人 DNS 服务器"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的连接受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"点按即可继续连接"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"已切换至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"设备会在<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>无法访问互联网时使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g>(可能需要支付相应的费用)。"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"已从<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切换至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"未知网络类型"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"系统网络连接资源"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"登录到WLAN网络"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"登录到网络"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 无法访问互联网"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"点按即可查看相关选项"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"此移动网络无法访问互联网"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"此网络无法访问互联网"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"无法访问私人 DNS 服务器"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的连接受限"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"点按即可继续连接"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"已切换至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"设备会在<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>无法访问互联网时使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g>(可能需要支付相应的费用)。"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"已从<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切换至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"移动数据"</item>
- <item msgid="5520925862115353992">"WLAN"</item>
- <item msgid="1055487873974272842">"蓝牙"</item>
- <item msgid="1616528372438698248">"以太网"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"移动数据"</item>
+ <item msgid="5624324321165953608">"WLAN"</item>
+ <item msgid="5667906231066981731">"蓝牙"</item>
+ <item msgid="346574747471703768">"以太网"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"未知网络类型"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml b/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
index 676404f..af3dccd 100644
--- a/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"登入 Wi-Fi 網絡"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"登入網絡"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>未有連接至互聯網"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"輕按即可查看選項"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"流動網絡並未連接互聯網"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"網絡並未連接互聯網"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"無法存取私人 DNS 伺服器"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>連線受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"仍要輕按以連結至此網絡"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"裝置會在 <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> 無法連線至互聯網時使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g> (可能需要支付相關費用)。"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"已從<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明網絡類型"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"系統連線資源"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"登入 Wi-Fi 網絡"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"登入網絡"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>未有連接至互聯網"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"輕按即可查看選項"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"流動網絡並未連接互聯網"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"網絡並未連接互聯網"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"無法存取私人 DNS 伺服器"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>連線受限"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"仍要輕按以連結至此網絡"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"裝置會在 <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> 無法連線至互聯網時使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g> (可能需要支付相關費用)。"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"已從<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"流動數據"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"藍牙"</item>
- <item msgid="1616528372438698248">"以太網"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"流動數據"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"藍牙"</item>
+ <item msgid="346574747471703768">"以太網絡"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"不明網絡類型"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml b/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
index f355138..6441707 100644
--- a/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"登入 Wi-Fi 網路"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"登入網路"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 沒有網際網路連線"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"輕觸即可查看選項"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"這個行動網路沒有網際網路連線"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"這個網路沒有網際網路連線"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"無法存取私人 DNS 伺服器"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的連線能力受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"輕觸即可繼續連線"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"裝置會在無法連上「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」時切換至「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」(可能需要支付相關費用)。"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"已從 <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> 切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明的網路類型"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"系統連線資源"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"登入 Wi-Fi 網路"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"登入網路"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 沒有網際網路連線"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"輕觸即可查看選項"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"這個行動網路沒有網際網路連線"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"這個網路沒有網際網路連線"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"無法存取私人 DNS 伺服器"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的連線能力受限"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"輕觸即可繼續連線"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"裝置會在無法連上「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」時切換至「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」(可能需要支付相關費用)。"</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"已從 <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> 切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"行動數據"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"藍牙"</item>
- <item msgid="1616528372438698248">"乙太網路"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="3004933964374161223">"行動數據"</item>
+ <item msgid="5624324321165953608">"Wi-Fi"</item>
+ <item msgid="5667906231066981731">"藍牙"</item>
+ <item msgid="346574747471703768">"乙太網路"</item>
+ <item msgid="5734728378097476003">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"不明的網路類型"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values-zu/strings.xml b/service/ServiceConnectivityResources/res/values-zu/strings.xml
index 55fefb7..b59f0d1 100644
--- a/service/ServiceConnectivityResources/res/values-zu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-zu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR 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">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Ngena ngemvume kunethiwekhi ye-Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Ngena ngemvume kunethiwekhi"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ayinakho ukufinyelela kwe-inthanethi"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Thepha ukuze uthole izinketho"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Inethiwekhi yeselula ayinakho ukufinyelela kwe-inthanethi"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Inethiwekhi ayinakho ukufinyelela kwenethiwekhi"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> inokuxhumeka okukhawulelwe"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Thepha ukuze uxhume noma kunjalo"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Kushintshelwe ku-<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Idivayisi isebenzisa i-<xliff:g id="NEW_NETWORK">%1$s</xliff:g> uma i-<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> inganakho ukufinyelela kwe-inthanethi. Kungasebenza izindleko."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Kushintshelewe kusuka ku-<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kuya ku-<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"uhlobo olungaziwa lwenethiwekhi"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Izinsiza Zokuxhumeka Zesistimu"</string>
+ <string name="wifi_available_sign_in" msgid="8041178343789805553">"Ngena ngemvume kunethiwekhi ye-Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="2622520134876355561">"Ngena ngemvume kunethiwekhi"</string>
+ <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="1326348603404555475">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ayinakho ukufinyelela kwe-inthanethi"</string>
+ <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Thepha ukuze uthole izinketho"</string>
+ <string name="mobile_no_internet" msgid="4087718456753201450">"Inethiwekhi yeselula ayinakho ukufinyelela kwe-inthanethi"</string>
+ <string name="other_networks_no_internet" msgid="5693932964749676542">"Inethiwekhi ayinakho ukufinyelela kwenethiwekhi"</string>
+ <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
+ <string name="network_partial_connectivity" msgid="5549503845834993258">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> inokuxhumeka okukhawulelwe"</string>
+ <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Thepha ukuze uxhume noma kunjalo"</string>
+ <string name="network_switch_metered" msgid="5016937523571166319">"Kushintshelwe ku-<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"Idivayisi isebenzisa i-<xliff:g id="NEW_NETWORK">%1$s</xliff:g> uma i-<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> inganakho ukufinyelela kwe-inthanethi. Kungasebenza izindleko."</string>
+ <string name="network_switch_metered_toast" msgid="70691146054130335">"Kushintshelewe kusuka ku-<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kuya ku-<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"idatha yeselula"</item>
- <item msgid="5520925862115353992">"I-Wi-Fi"</item>
- <item msgid="1055487873974272842">"I-Bluetooth"</item>
- <item msgid="1616528372438698248">"I-Ethernet"</item>
- <item msgid="9177085807664964627">"I-VPN"</item>
+ <item msgid="3004933964374161223">"idatha yeselula"</item>
+ <item msgid="5624324321165953608">"I-Wi-Fi"</item>
+ <item msgid="5667906231066981731">"I-Bluetooth"</item>
+ <item msgid="346574747471703768">"I-Ethernet"</item>
+ <item msgid="5734728378097476003">"I-VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"uhlobo olungaziwa lwenethiwekhi"</string>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index b22457a..f8f86a2 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -125,4 +125,15 @@
details on what is happening. -->
<bool name="config_partialConnectivityNotifiedAsNoInternet">false</bool>
+ <!-- Whether the cell radio of the device is capable of timesharing.
+
+ Whether the cell radio is capable of timesharing between two different networks
+ even for a few seconds. When this is false, the networking stack will ask telephony
+ networks to disconnect immediately, instead of lingering, when outscored by some
+ other telephony network (typically on another subscription). This deprives apps
+ of a chance to gracefully migrate to the new network and degrades the experience
+ for apps, so it should only be set to false when timesharing on the cell radio has
+ extreme adverse effects on performance of the new network.
+ -->
+ <bool translatable="false" name="config_cellular_radio_timesharing_capable">true</bool>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index 5af13d7..e5010d7 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -36,6 +36,7 @@
<item type="bool" name="config_partialConnectivityNotifiedAsNoInternet"/>
<item type="drawable" name="stat_notify_wifi_in_range"/>
<item type="drawable" name="stat_notify_rssi_in_range"/>
+ <item type="bool" name="config_cellular_radio_timesharing_capable" />
</policy>
</overlayable>
</resources>
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index 4ba6837..e5d1a88 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -8,10 +8,7 @@
# the one in com.android.internal.util
rule android.util.IndentingPrintWriter* com.android.connectivity.@0
rule com.android.internal.util.IndentingPrintWriter* com.android.connectivity.@0
-rule com.android.internal.util.IState* com.android.connectivity.@0
rule com.android.internal.util.MessageUtils* com.android.connectivity.@0
-rule com.android.internal.util.State* com.android.connectivity.@0
-rule com.android.internal.util.StateMachine* com.android.connectivity.@0
rule com.android.internal.util.WakeupMessage* com.android.connectivity.@0
rule com.android.internal.messages.** com.android.connectivity.@0
diff --git a/service/jni/com_android_net_module_util/onload.cpp b/service/jni/com_android_net_module_util/onload.cpp
new file mode 100644
index 0000000..1d17622
--- /dev/null
+++ b/service/jni/com_android_net_module_util/onload.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include <log/log.h>
+
+namespace android {
+
+int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name);
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("GetEnv failed");
+ return JNI_ERR;
+ }
+
+ if (register_com_android_net_module_util_BpfMap(env,
+ "com/android/connectivity/com/android/net/module/util/BpfMap") < 0) return JNI_ERR;
+
+ return JNI_VERSION_1_6;
+}
+
+};
diff --git a/service/jni/com_android_server_TestNetworkService.cpp b/service/jni/com_android_server_TestNetworkService.cpp
index e7a40e5..1a0de32 100644
--- a/service/jni/com_android_server_TestNetworkService.cpp
+++ b/service/jni/com_android_server_TestNetworkService.cpp
@@ -66,6 +66,8 @@
// Activate interface using an unconnected datagram socket.
base::unique_fd inet6CtrlSock(socket(AF_INET6, SOCK_DGRAM, 0));
ifr.ifr_flags = IFF_UP;
+ // Mark TAP interfaces as supporting multicast
+ if (!isTun) ifr.ifr_flags |= IFF_MULTICAST;
if (ioctl(inet6CtrlSock.get(), SIOCSIFFLAGS, &ifr)) {
throwException(env, errno, "activating", ifr.ifr_name);
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
new file mode 100644
index 0000000..a9d7946
--- /dev/null
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
+#include <linux/ioctl.h>
+#include <nativehelper/JNIHelp.h>
+#include <net/if.h>
+
+#include <netjniutils/netjniutils.h>
+
+#include "libclat/clatutils.h"
+#include "nativehelper/scoped_utf_chars.h"
+
+// Sync from system/netd/include/netid_client.h
+#define MARK_UNSET 0u
+
+namespace android {
+static void throwIOException(JNIEnv* env, const char* msg, int error) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "%s: %s", msg, strerror(error));
+}
+
+jstring com_android_server_connectivity_ClatCoordinator_selectIpv4Address(JNIEnv* env,
+ jobject clazz,
+ jstring v4addr,
+ jint prefixlen) {
+ ScopedUtfChars address(env, v4addr);
+ in_addr ip;
+ if (inet_pton(AF_INET, address.c_str(), &ip) != 1) {
+ throwIOException(env, "invalid address", EINVAL);
+ return nullptr;
+ }
+
+ // Pick an IPv4 address.
+ // TODO: this picks the address based on other addresses that are assigned to interfaces, but
+ // the address is only actually assigned to an interface once clatd starts up. So we could end
+ // up with two clatd instances with the same IPv4 address.
+ // Stop doing this and instead pick a free one from the kV4Addr pool.
+ in_addr v4 = {net::clat::selectIpv4Address(ip, prefixlen)};
+ if (v4.s_addr == INADDR_NONE) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "No free IPv4 address in %s/%d",
+ address.c_str(), prefixlen);
+ return nullptr;
+ }
+
+ char addrstr[INET_ADDRSTRLEN];
+ if (!inet_ntop(AF_INET, (void*)&v4, addrstr, sizeof(addrstr))) {
+ throwIOException(env, "invalid address", EADDRNOTAVAIL);
+ return nullptr;
+ }
+ return env->NewStringUTF(addrstr);
+}
+
+// Picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix.
+jstring com_android_server_connectivity_ClatCoordinator_generateIpv6Address(
+ JNIEnv* env, jobject clazz, jstring ifaceStr, jstring v4Str, jstring prefix64Str) {
+ ScopedUtfChars iface(env, ifaceStr);
+ ScopedUtfChars addr4(env, v4Str);
+ ScopedUtfChars prefix64(env, prefix64Str);
+
+ if (iface.c_str() == nullptr) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid null interface name");
+ return nullptr;
+ }
+
+ in_addr v4;
+ if (inet_pton(AF_INET, addr4.c_str(), &v4) != 1) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid clat v4 address %s",
+ addr4.c_str());
+ return nullptr;
+ }
+
+ in6_addr nat64Prefix;
+ if (inet_pton(AF_INET6, prefix64.c_str(), &nat64Prefix) != 1) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid prefix %s", prefix64.c_str());
+ return nullptr;
+ }
+
+ in6_addr v6;
+ if (net::clat::generateIpv6Address(iface.c_str(), v4, nat64Prefix, &v6)) {
+ jniThrowExceptionFmt(env, "java/io/IOException",
+ "Unable to find global source address on %s for %s", iface.c_str(),
+ prefix64.c_str());
+ return nullptr;
+ }
+
+ char addrstr[INET6_ADDRSTRLEN];
+ if (!inet_ntop(AF_INET6, (void*)&v6, addrstr, sizeof(addrstr))) {
+ throwIOException(env, "invalid address", EADDRNOTAVAIL);
+ return nullptr;
+ }
+ return env->NewStringUTF(addrstr);
+}
+
+static jint com_android_server_connectivity_ClatCoordinator_createTunInterface(JNIEnv* env,
+ jobject clazz,
+ jstring tuniface) {
+ ScopedUtfChars v4interface(env, tuniface);
+
+ // open the tun device in non blocking mode as required by clatd
+ jint fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ if (fd == -1) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "open tun device failed (%s)",
+ strerror(errno));
+ return -1;
+ }
+
+ struct ifreq ifr = {
+ .ifr_flags = IFF_TUN,
+ };
+ strlcpy(ifr.ifr_name, v4interface.c_str(), sizeof(ifr.ifr_name));
+
+ if (ioctl(fd, TUNSETIFF, &ifr, sizeof(ifr))) {
+ close(fd);
+ jniThrowExceptionFmt(env, "java/io/IOException", "ioctl(TUNSETIFF) failed (%s)",
+ strerror(errno));
+ return -1;
+ }
+
+ return fd;
+}
+
+static jint com_android_server_connectivity_ClatCoordinator_detectMtu(JNIEnv* env, jobject clazz,
+ jstring platSubnet,
+ jint plat_suffix, jint mark) {
+ ScopedUtfChars platSubnetStr(env, platSubnet);
+
+ in6_addr plat_subnet;
+ if (inet_pton(AF_INET6, platSubnetStr.c_str(), &plat_subnet) != 1) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid plat prefix address %s",
+ platSubnetStr.c_str());
+ return -1;
+ }
+
+ int ret = net::clat::detect_mtu(&plat_subnet, plat_suffix, mark);
+ if (ret < 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "detect mtu failed: %s", strerror(-ret));
+ return -1;
+ }
+
+ return ret;
+}
+
+static jint com_android_server_connectivity_ClatCoordinator_openPacketSocket(JNIEnv* env,
+ jobject clazz) {
+ // Will eventually be bound to htons(ETH_P_IPV6) protocol,
+ // but only after appropriate bpf filter is attached.
+ int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sock < 0) {
+ throwIOException(env, "packet socket failed", errno);
+ return -1;
+ }
+ return sock;
+}
+
+static jint com_android_server_connectivity_ClatCoordinator_openRawSocket6(JNIEnv* env,
+ jobject clazz,
+ jint mark) {
+ int sock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW);
+ if (sock < 0) {
+ throwIOException(env, "raw socket failed", errno);
+ return -1;
+ }
+
+ // TODO: check the mark validation
+ if (mark != MARK_UNSET && setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
+ throwIOException(env, "could not set mark on raw socket", errno);
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+static void com_android_server_connectivity_ClatCoordinator_addAnycastSetsockopt(
+ JNIEnv* env, jobject clazz, jobject javaFd, jstring addr6, jint ifindex) {
+ int sock = netjniutils::GetNativeFileDescriptor(env, javaFd);
+ if (sock < 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
+ return;
+ }
+
+ ScopedUtfChars addrStr(env, addr6);
+
+ in6_addr addr;
+ if (inet_pton(AF_INET6, addrStr.c_str(), &addr) != 1) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid IPv6 address %s",
+ addrStr.c_str());
+ return;
+ }
+
+ struct ipv6_mreq mreq = {addr, ifindex};
+ int ret = setsockopt(sock, SOL_IPV6, IPV6_JOIN_ANYCAST, &mreq, sizeof(mreq));
+ if (ret) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "setsockopt IPV6_JOIN_ANYCAST failed: %s",
+ strerror(errno));
+ return;
+ }
+}
+
+static void com_android_server_connectivity_ClatCoordinator_configurePacketSocket(
+ JNIEnv* env, jobject clazz, jobject javaFd, jstring addr6, jint ifindex) {
+ ScopedUtfChars addrStr(env, addr6);
+
+ int sock = netjniutils::GetNativeFileDescriptor(env, javaFd);
+ if (sock < 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
+ return;
+ }
+
+ in6_addr addr;
+ if (inet_pton(AF_INET6, addrStr.c_str(), &addr) != 1) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid IPv6 address %s",
+ addrStr.c_str());
+ return;
+ }
+
+ int ret = net::clat::configure_packet_socket(sock, &addr, ifindex);
+ if (ret < 0) {
+ throwIOException(env, "configure packet socket failed", -ret);
+ return;
+ }
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ {"native_selectIpv4Address", "(Ljava/lang/String;I)Ljava/lang/String;",
+ (void*)com_android_server_connectivity_ClatCoordinator_selectIpv4Address},
+ {"native_generateIpv6Address",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+ (void*)com_android_server_connectivity_ClatCoordinator_generateIpv6Address},
+ {"native_createTunInterface", "(Ljava/lang/String;)I",
+ (void*)com_android_server_connectivity_ClatCoordinator_createTunInterface},
+ {"native_detectMtu", "(Ljava/lang/String;II)I",
+ (void*)com_android_server_connectivity_ClatCoordinator_detectMtu},
+ {"native_openPacketSocket", "()I",
+ (void*)com_android_server_connectivity_ClatCoordinator_openPacketSocket},
+ {"native_openRawSocket6", "(I)I",
+ (void*)com_android_server_connectivity_ClatCoordinator_openRawSocket6},
+ {"native_addAnycastSetsockopt", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
+ (void*)com_android_server_connectivity_ClatCoordinator_addAnycastSetsockopt},
+ {"native_configurePacketSocket", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
+ (void*)com_android_server_connectivity_ClatCoordinator_configurePacketSocket},
+};
+
+int register_android_server_connectivity_ClatCoordinator(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/connectivity/ClatCoordinator",
+ gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/service/jni/onload.cpp b/service/jni/onload.cpp
index 0012879..04d9671 100644
--- a/service/jni/onload.cpp
+++ b/service/jni/onload.cpp
@@ -20,6 +20,7 @@
namespace android {
int register_android_server_TestNetworkService(JNIEnv* env);
+int register_android_server_connectivity_ClatCoordinator(JNIEnv* env);
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv *env;
@@ -32,6 +33,10 @@
return JNI_ERR;
}
+ if (register_android_server_connectivity_ClatCoordinator(env) < 0) {
+ return JNI_ERR;
+ }
+
return JNI_VERSION_1_6;
}
diff --git a/service/lint-baseline.xml b/service/lint-baseline.xml
deleted file mode 100644
index df57c22..0000000
--- a/service/lint-baseline.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"
- errorLine1=" if (tm.isDataCapable()) {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
- line="791"
- column="20"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.Context#sendStickyBroadcast`"
- errorLine1=" mUserAllContext.sendStickyBroadcast(intent, options);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
- line="2693"
- column="33"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.pm.PackageManager#getTargetSdkVersion`"
- errorLine1=" final int callingVersion = pm.getTargetSdkVersion(callingPackageName);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
- line="5894"
- column="43"/>
- </issue>
-
-</issues>
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
new file mode 100644
index 0000000..c24a41b
--- /dev/null
+++ b/service/native/TrafficController.cpp
@@ -0,0 +1,910 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TrafficController"
+#include <inttypes.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/inet_diag.h>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
+#include <linux/unistd.h>
+#include <net/if.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <mutex>
+#include <unordered_set>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <netdutils/StatusOr.h>
+
+#include <netdutils/Syscalls.h>
+#include <netdutils/Utils.h>
+#include "TrafficController.h"
+#include "bpf/BpfMap.h"
+
+#include "netdutils/DumpWriter.h"
+
+namespace android {
+namespace net {
+
+using base::StringPrintf;
+using base::unique_fd;
+using bpf::BpfMap;
+using bpf::OVERFLOW_COUNTERSET;
+using bpf::synchronizeKernelRCU;
+using netdutils::DumpWriter;
+using netdutils::getIfaceList;
+using netdutils::NetlinkListener;
+using netdutils::NetlinkListenerInterface;
+using netdutils::ScopedIndent;
+using netdutils::Slice;
+using netdutils::sSyscalls;
+using netdutils::Status;
+using netdutils::statusFromErrno;
+using netdutils::StatusOr;
+using netdutils::status::ok;
+
+constexpr int kSockDiagMsgType = SOCK_DIAG_BY_FAMILY;
+constexpr int kSockDiagDoneMsgType = NLMSG_DONE;
+
+const char* TrafficController::LOCAL_DOZABLE = "fw_dozable";
+const char* TrafficController::LOCAL_STANDBY = "fw_standby";
+const char* TrafficController::LOCAL_POWERSAVE = "fw_powersave";
+const char* TrafficController::LOCAL_RESTRICTED = "fw_restricted";
+
+static_assert(BPF_PERMISSION_INTERNET == INetd::PERMISSION_INTERNET,
+ "Mismatch between BPF and AIDL permissions: PERMISSION_INTERNET");
+static_assert(BPF_PERMISSION_UPDATE_DEVICE_STATS == INetd::PERMISSION_UPDATE_DEVICE_STATS,
+ "Mismatch between BPF and AIDL permissions: PERMISSION_UPDATE_DEVICE_STATS");
+
+#define FLAG_MSG_TRANS(result, flag, value) \
+ do { \
+ if ((value) & (flag)) { \
+ (result).append(" " #flag); \
+ (value) &= ~(flag); \
+ } \
+ } while (0)
+
+const std::string uidMatchTypeToString(uint8_t match) {
+ std::string matchType;
+ FLAG_MSG_TRANS(matchType, HAPPY_BOX_MATCH, match);
+ FLAG_MSG_TRANS(matchType, PENALTY_BOX_MATCH, match);
+ FLAG_MSG_TRANS(matchType, DOZABLE_MATCH, match);
+ FLAG_MSG_TRANS(matchType, STANDBY_MATCH, match);
+ FLAG_MSG_TRANS(matchType, POWERSAVE_MATCH, match);
+ FLAG_MSG_TRANS(matchType, RESTRICTED_MATCH, match);
+ FLAG_MSG_TRANS(matchType, IIF_MATCH, match);
+ if (match) {
+ return StringPrintf("Unknown match: %u", match);
+ }
+ return matchType;
+}
+
+bool TrafficController::hasUpdateDeviceStatsPermission(uid_t uid) {
+ // This implementation is the same logic as method ActivityManager#checkComponentPermission.
+ // It implies that the calling uid can never be the same as PER_USER_RANGE.
+ uint32_t appId = uid % PER_USER_RANGE;
+ return ((appId == AID_ROOT) || (appId == AID_SYSTEM) ||
+ mPrivilegedUser.find(appId) != mPrivilegedUser.end());
+}
+
+const std::string UidPermissionTypeToString(int permission) {
+ if (permission == INetd::PERMISSION_NONE) {
+ return "PERMISSION_NONE";
+ }
+ if (permission == INetd::PERMISSION_UNINSTALLED) {
+ // This should never appear in the map, complain loudly if it does.
+ return "PERMISSION_UNINSTALLED error!";
+ }
+ std::string permissionType;
+ FLAG_MSG_TRANS(permissionType, BPF_PERMISSION_INTERNET, permission);
+ FLAG_MSG_TRANS(permissionType, BPF_PERMISSION_UPDATE_DEVICE_STATS, permission);
+ if (permission) {
+ return StringPrintf("Unknown permission: %u", permission);
+ }
+ return permissionType;
+}
+
+StatusOr<std::unique_ptr<NetlinkListenerInterface>> TrafficController::makeSkDestroyListener() {
+ const auto& sys = sSyscalls.get();
+ ASSIGN_OR_RETURN(auto event, sys.eventfd(0, EFD_CLOEXEC));
+ const int domain = AF_NETLINK;
+ const int type = SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK;
+ const int protocol = NETLINK_INET_DIAG;
+ ASSIGN_OR_RETURN(auto sock, sys.socket(domain, type, protocol));
+
+ // TODO: if too many sockets are closed too quickly, we can overflow the socket buffer, and
+ // some entries in mCookieTagMap will not be freed. In order to fix this we would need to
+ // periodically dump all sockets and remove the tag entries for sockets that have been closed.
+ // For now, set a large-enough buffer that we can close hundreds of sockets without getting
+ // ENOBUFS and leaking mCookieTagMap entries.
+ int rcvbuf = 512 * 1024;
+ auto ret = sys.setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+ if (!ret.ok()) {
+ ALOGW("Failed to set SkDestroyListener buffer size to %d: %s", rcvbuf, ret.msg().c_str());
+ }
+
+ sockaddr_nl addr = {
+ .nl_family = AF_NETLINK,
+ .nl_groups = 1 << (SKNLGRP_INET_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET_UDP_DESTROY - 1) |
+ 1 << (SKNLGRP_INET6_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET6_UDP_DESTROY - 1)};
+ RETURN_IF_NOT_OK(sys.bind(sock, addr));
+
+ const sockaddr_nl kernel = {.nl_family = AF_NETLINK};
+ RETURN_IF_NOT_OK(sys.connect(sock, kernel));
+
+ std::unique_ptr<NetlinkListenerInterface> listener =
+ std::make_unique<NetlinkListener>(std::move(event), std::move(sock), "SkDestroyListen");
+
+ return listener;
+}
+
+Status TrafficController::initMaps() {
+ std::lock_guard guard(mMutex);
+
+ RETURN_IF_NOT_OK(mCookieTagMap.init(COOKIE_TAG_MAP_PATH));
+ RETURN_IF_NOT_OK(mUidCounterSetMap.init(UID_COUNTERSET_MAP_PATH));
+ RETURN_IF_NOT_OK(mAppUidStatsMap.init(APP_UID_STATS_MAP_PATH));
+ RETURN_IF_NOT_OK(mStatsMapA.init(STATS_MAP_A_PATH));
+ RETURN_IF_NOT_OK(mStatsMapB.init(STATS_MAP_B_PATH));
+ RETURN_IF_NOT_OK(mIfaceIndexNameMap.init(IFACE_INDEX_NAME_MAP_PATH));
+ RETURN_IF_NOT_OK(mIfaceStatsMap.init(IFACE_STATS_MAP_PATH));
+
+ RETURN_IF_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH));
+ RETURN_IF_NOT_OK(
+ mConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY, DEFAULT_CONFIG, BPF_ANY));
+ RETURN_IF_NOT_OK(mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, SELECT_MAP_A,
+ BPF_ANY));
+
+ RETURN_IF_NOT_OK(mUidOwnerMap.init(UID_OWNER_MAP_PATH));
+ RETURN_IF_NOT_OK(mUidOwnerMap.clear());
+ RETURN_IF_NOT_OK(mUidPermissionMap.init(UID_PERMISSION_MAP_PATH));
+
+ return netdutils::status::ok;
+}
+
+Status TrafficController::start() {
+ /* When netd restarts from a crash without total system reboot, the program
+ * is still attached to the cgroup, detach it so the program can be freed
+ * and we can load and attach new program into the target cgroup.
+ *
+ * TODO: Scrape existing socket when run-time restart and clean up the map
+ * if the socket no longer exist
+ */
+
+ RETURN_IF_NOT_OK(initMaps());
+
+ // Fetch the list of currently-existing interfaces. At this point NetlinkHandler is
+ // already running, so it will call addInterface() when any new interface appears.
+ std::map<std::string, uint32_t> ifacePairs;
+ ASSIGN_OR_RETURN(ifacePairs, getIfaceList());
+ for (const auto& ifacePair:ifacePairs) {
+ addInterface(ifacePair.first.c_str(), ifacePair.second);
+ }
+
+ auto result = makeSkDestroyListener();
+ if (!isOk(result)) {
+ ALOGE("Unable to create SkDestroyListener: %s", toString(result).c_str());
+ } else {
+ mSkDestroyListener = std::move(result.value());
+ }
+ // Rx handler extracts nfgenmsg looks up and invokes registered dispatch function.
+ const auto rxHandler = [this](const nlmsghdr&, const Slice msg) {
+ std::lock_guard guard(mMutex);
+ inet_diag_msg diagmsg = {};
+ if (extract(msg, diagmsg) < sizeof(inet_diag_msg)) {
+ ALOGE("Unrecognized netlink message: %s", toString(msg).c_str());
+ return;
+ }
+ uint64_t sock_cookie = static_cast<uint64_t>(diagmsg.id.idiag_cookie[0]) |
+ (static_cast<uint64_t>(diagmsg.id.idiag_cookie[1]) << 32);
+
+ Status s = mCookieTagMap.deleteValue(sock_cookie);
+ if (!isOk(s) && s.code() != ENOENT) {
+ ALOGE("Failed to delete cookie %" PRIx64 ": %s", sock_cookie, toString(s).c_str());
+ return;
+ }
+ };
+ expectOk(mSkDestroyListener->subscribe(kSockDiagMsgType, rxHandler));
+
+ // In case multiple netlink message comes in as a stream, we need to handle the rxDone message
+ // properly.
+ const auto rxDoneHandler = [](const nlmsghdr&, const Slice msg) {
+ // Ignore NLMSG_DONE messages
+ inet_diag_msg diagmsg = {};
+ extract(msg, diagmsg);
+ };
+ expectOk(mSkDestroyListener->subscribe(kSockDiagDoneMsgType, rxDoneHandler));
+
+ return netdutils::status::ok;
+}
+
+int TrafficController::setCounterSet(int counterSetNum, uid_t uid, uid_t callingUid) {
+ if (counterSetNum < 0 || counterSetNum >= OVERFLOW_COUNTERSET) return -EINVAL;
+
+ std::lock_guard guard(mMutex);
+ if (!hasUpdateDeviceStatsPermission(callingUid)) return -EPERM;
+
+ // The default counter set for all uid is 0, so deleting the current counterset for that uid
+ // will automatically set it to 0.
+ if (counterSetNum == 0) {
+ Status res = mUidCounterSetMap.deleteValue(uid);
+ if (isOk(res) || (!isOk(res) && res.code() == ENOENT)) {
+ return 0;
+ } else {
+ ALOGE("Failed to delete the counterSet: %s\n", strerror(res.code()));
+ return -res.code();
+ }
+ }
+ uint8_t tmpCounterSetNum = (uint8_t)counterSetNum;
+ Status res = mUidCounterSetMap.writeValue(uid, tmpCounterSetNum, BPF_ANY);
+ if (!isOk(res)) {
+ ALOGE("Failed to set the counterSet: %s, fd: %d", strerror(res.code()),
+ mUidCounterSetMap.getMap().get());
+ return -res.code();
+ }
+ return 0;
+}
+
+// This method only get called by system_server when an app get uinstalled, it
+// is called inside removeUidsLocked() while holding mStatsLock. So it is safe
+// to iterate and modify the stats maps.
+int TrafficController::deleteTagData(uint32_t tag, uid_t uid, uid_t callingUid) {
+ std::lock_guard guard(mMutex);
+ if (!hasUpdateDeviceStatsPermission(callingUid)) return -EPERM;
+
+ // First we go through the cookieTagMap to delete the target uid tag combination. Or delete all
+ // the tags related to the uid if the tag is 0.
+ const auto deleteMatchedCookieEntries = [uid, tag](const uint64_t& key,
+ const UidTagValue& value,
+ BpfMap<uint64_t, UidTagValue>& map) {
+ if (value.uid == uid && (value.tag == tag || tag == 0)) {
+ auto res = map.deleteValue(key);
+ if (res.ok() || (res.error().code() == ENOENT)) {
+ return base::Result<void>();
+ }
+ ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", key,
+ strerror(res.error().code()));
+ }
+ // Move forward to next cookie in the map.
+ return base::Result<void>();
+ };
+ mCookieTagMap.iterateWithValue(deleteMatchedCookieEntries);
+ // Now we go through the Tag stats map and delete the data entry with correct uid and tag
+ // combination. Or all tag stats under that uid if the target tag is 0.
+ const auto deleteMatchedUidTagEntries = [uid, tag](const StatsKey& key,
+ BpfMap<StatsKey, StatsValue>& map) {
+ if (key.uid == uid && (key.tag == tag || tag == 0)) {
+ auto res = map.deleteValue(key);
+ if (res.ok() || (res.error().code() == ENOENT)) {
+ //Entry is deleted, use the current key to get a new nextKey;
+ return base::Result<void>();
+ }
+ ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", key.uid, key.tag,
+ strerror(res.error().code()));
+ }
+ return base::Result<void>();
+ };
+ mStatsMapB.iterate(deleteMatchedUidTagEntries);
+ mStatsMapA.iterate(deleteMatchedUidTagEntries);
+ // If the tag is not zero, we already deleted all the data entry required. If tag is 0, we also
+ // need to delete the stats stored in uidStatsMap and counterSet map.
+ if (tag != 0) return 0;
+
+ auto res = mUidCounterSetMap.deleteValue(uid);
+ if (!res.ok() && res.error().code() != ENOENT) {
+ ALOGE("Failed to delete counterSet data(uid=%u, tag=%u): %s\n", uid, tag,
+ strerror(res.error().code()));
+ }
+
+ auto deleteAppUidStatsEntry = [uid](const uint32_t& key,
+ BpfMap<uint32_t, StatsValue>& map) -> base::Result<void> {
+ if (key == uid) {
+ auto res = map.deleteValue(key);
+ if (res.ok() || (res.error().code() == ENOENT)) {
+ return {};
+ }
+ ALOGE("Failed to delete data(uid=%u): %s", key, strerror(res.error().code()));
+ }
+ return {};
+ };
+ mAppUidStatsMap.iterate(deleteAppUidStatsEntry);
+ return 0;
+}
+
+int TrafficController::addInterface(const char* name, uint32_t ifaceIndex) {
+ IfaceValue iface;
+ if (ifaceIndex == 0) {
+ ALOGE("Unknown interface %s(%d)", name, ifaceIndex);
+ return -1;
+ }
+
+ strlcpy(iface.name, name, sizeof(IfaceValue));
+ Status res = mIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY);
+ if (!isOk(res)) {
+ ALOGE("Failed to add iface %s(%d): %s", name, ifaceIndex, strerror(res.code()));
+ return -res.code();
+ }
+ return 0;
+}
+
+Status TrafficController::updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid, FirewallRule rule,
+ FirewallType type) {
+ std::lock_guard guard(mMutex);
+ if ((rule == ALLOW && type == ALLOWLIST) || (rule == DENY && type == DENYLIST)) {
+ RETURN_IF_NOT_OK(addRule(uid, match));
+ } else if ((rule == ALLOW && type == DENYLIST) || (rule == DENY && type == ALLOWLIST)) {
+ RETURN_IF_NOT_OK(removeRule(uid, match));
+ } else {
+ //Cannot happen.
+ return statusFromErrno(EINVAL, "");
+ }
+ return netdutils::status::ok;
+}
+
+Status TrafficController::removeRule(uint32_t uid, UidOwnerMatchType match) {
+ auto oldMatch = mUidOwnerMap.readValue(uid);
+ if (oldMatch.ok()) {
+ UidOwnerValue newMatch = {
+ .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif,
+ .rule = static_cast<uint8_t>(oldMatch.value().rule & ~match),
+ };
+ if (newMatch.rule == 0) {
+ RETURN_IF_NOT_OK(mUidOwnerMap.deleteValue(uid));
+ } else {
+ RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY));
+ }
+ } else {
+ return statusFromErrno(ENOENT, StringPrintf("uid: %u does not exist in map", uid));
+ }
+ return netdutils::status::ok;
+}
+
+Status TrafficController::addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif) {
+ // iif should be non-zero if and only if match == MATCH_IIF
+ if (match == IIF_MATCH && iif == 0) {
+ return statusFromErrno(EINVAL, "Interface match must have nonzero interface index");
+ } else if (match != IIF_MATCH && iif != 0) {
+ return statusFromErrno(EINVAL, "Non-interface match must have zero interface index");
+ }
+ auto oldMatch = mUidOwnerMap.readValue(uid);
+ if (oldMatch.ok()) {
+ UidOwnerValue newMatch = {
+ .iif = iif ? iif : oldMatch.value().iif,
+ .rule = static_cast<uint8_t>(oldMatch.value().rule | match),
+ };
+ RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY));
+ } else {
+ UidOwnerValue newMatch = {
+ .iif = iif,
+ .rule = static_cast<uint8_t>(match),
+ };
+ RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY));
+ }
+ return netdutils::status::ok;
+}
+
+Status TrafficController::updateUidOwnerMap(const std::vector<uint32_t>& appUids,
+ UidOwnerMatchType matchType, IptOp op) {
+ std::lock_guard guard(mMutex);
+ for (uint32_t uid : appUids) {
+ if (op == IptOpDelete) {
+ RETURN_IF_NOT_OK(removeRule(uid, matchType));
+ } else if (op == IptOpInsert) {
+ RETURN_IF_NOT_OK(addRule(uid, matchType));
+ } else {
+ // Cannot happen.
+ return statusFromErrno(EINVAL, StringPrintf("invalid IptOp: %d, %d", op, matchType));
+ }
+ }
+ return netdutils::status::ok;
+}
+
+FirewallType TrafficController::getFirewallType(ChildChain chain) {
+ switch (chain) {
+ case DOZABLE:
+ return ALLOWLIST;
+ case STANDBY:
+ return DENYLIST;
+ case POWERSAVE:
+ return ALLOWLIST;
+ case RESTRICTED:
+ return ALLOWLIST;
+ case NONE:
+ default:
+ return DENYLIST;
+ }
+}
+
+int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule,
+ FirewallType type) {
+ Status res;
+ switch (chain) {
+ case DOZABLE:
+ res = updateOwnerMapEntry(DOZABLE_MATCH, uid, rule, type);
+ break;
+ case STANDBY:
+ res = updateOwnerMapEntry(STANDBY_MATCH, uid, rule, type);
+ break;
+ case POWERSAVE:
+ res = updateOwnerMapEntry(POWERSAVE_MATCH, uid, rule, type);
+ break;
+ case RESTRICTED:
+ res = updateOwnerMapEntry(RESTRICTED_MATCH, uid, rule, type);
+ break;
+ case NONE:
+ default:
+ ALOGW("Unknown child chain: %d", chain);
+ return -EINVAL;
+ }
+ if (!isOk(res)) {
+ ALOGE("change uid(%u) rule of %d failed: %s, rule: %d, type: %d", uid, chain,
+ res.msg().c_str(), rule, type);
+ return -res.code();
+ }
+ return 0;
+}
+
+Status TrafficController::replaceRulesInMap(const UidOwnerMatchType match,
+ const std::vector<int32_t>& uids) {
+ std::lock_guard guard(mMutex);
+ std::set<int32_t> uidSet(uids.begin(), uids.end());
+ std::vector<uint32_t> uidsToDelete;
+ auto getUidsToDelete = [&uidsToDelete, &uidSet](const uint32_t& key,
+ const BpfMap<uint32_t, UidOwnerValue>&) {
+ if (uidSet.find((int32_t) key) == uidSet.end()) {
+ uidsToDelete.push_back(key);
+ }
+ return base::Result<void>();
+ };
+ RETURN_IF_NOT_OK(mUidOwnerMap.iterate(getUidsToDelete));
+
+ for(auto uid : uidsToDelete) {
+ RETURN_IF_NOT_OK(removeRule(uid, match));
+ }
+
+ for (auto uid : uids) {
+ RETURN_IF_NOT_OK(addRule(uid, match));
+ }
+ return netdutils::status::ok;
+}
+
+Status TrafficController::addUidInterfaceRules(const int iif,
+ const std::vector<int32_t>& uidsToAdd) {
+ if (!iif) {
+ return statusFromErrno(EINVAL, "Interface rule must specify interface");
+ }
+ std::lock_guard guard(mMutex);
+
+ for (auto uid : uidsToAdd) {
+ netdutils::Status result = addRule(uid, IIF_MATCH, iif);
+ if (!isOk(result)) {
+ ALOGW("addRule failed(%d): uid=%d iif=%d", result.code(), uid, iif);
+ }
+ }
+ return netdutils::status::ok;
+}
+
+Status TrafficController::removeUidInterfaceRules(const std::vector<int32_t>& uidsToDelete) {
+ std::lock_guard guard(mMutex);
+
+ for (auto uid : uidsToDelete) {
+ netdutils::Status result = removeRule(uid, IIF_MATCH);
+ if (!isOk(result)) {
+ ALOGW("removeRule failed(%d): uid=%d", result.code(), uid);
+ }
+ }
+ return netdutils::status::ok;
+}
+
+int TrafficController::replaceUidOwnerMap(const std::string& name, bool isAllowlist __unused,
+ const std::vector<int32_t>& uids) {
+ // FirewallRule rule = isAllowlist ? ALLOW : DENY;
+ // FirewallType type = isAllowlist ? ALLOWLIST : DENYLIST;
+ Status res;
+ if (!name.compare(LOCAL_DOZABLE)) {
+ res = replaceRulesInMap(DOZABLE_MATCH, uids);
+ } else if (!name.compare(LOCAL_STANDBY)) {
+ res = replaceRulesInMap(STANDBY_MATCH, uids);
+ } else if (!name.compare(LOCAL_POWERSAVE)) {
+ res = replaceRulesInMap(POWERSAVE_MATCH, uids);
+ } else if (!name.compare(LOCAL_RESTRICTED)) {
+ res = replaceRulesInMap(RESTRICTED_MATCH, uids);
+ } else {
+ ALOGE("unknown chain name: %s", name.c_str());
+ return -EINVAL;
+ }
+ if (!isOk(res)) {
+ ALOGE("Failed to clean up chain: %s: %s", name.c_str(), res.msg().c_str());
+ return -res.code();
+ }
+ return 0;
+}
+
+int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) {
+ std::lock_guard guard(mMutex);
+ uint32_t key = UID_RULES_CONFIGURATION_KEY;
+ auto oldConfiguration = mConfigurationMap.readValue(key);
+ if (!oldConfiguration.ok()) {
+ ALOGE("Cannot read the old configuration from map: %s",
+ oldConfiguration.error().message().c_str());
+ return -oldConfiguration.error().code();
+ }
+ Status res;
+ BpfConfig newConfiguration;
+ uint8_t match;
+ switch (chain) {
+ case DOZABLE:
+ match = DOZABLE_MATCH;
+ break;
+ case STANDBY:
+ match = STANDBY_MATCH;
+ break;
+ case POWERSAVE:
+ match = POWERSAVE_MATCH;
+ break;
+ case RESTRICTED:
+ match = RESTRICTED_MATCH;
+ break;
+ default:
+ return -EINVAL;
+ }
+ newConfiguration =
+ enable ? (oldConfiguration.value() | match) : (oldConfiguration.value() & (~match));
+ res = mConfigurationMap.writeValue(key, newConfiguration, BPF_EXIST);
+ if (!isOk(res)) {
+ ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, res.msg().c_str());
+ }
+ return -res.code();
+}
+
+Status TrafficController::swapActiveStatsMap() {
+ std::lock_guard guard(mMutex);
+
+ uint32_t key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
+ auto oldConfiguration = mConfigurationMap.readValue(key);
+ if (!oldConfiguration.ok()) {
+ ALOGE("Cannot read the old configuration from map: %s",
+ oldConfiguration.error().message().c_str());
+ return Status(oldConfiguration.error().code(), oldConfiguration.error().message());
+ }
+
+ // Write to the configuration map to inform the kernel eBPF program to switch
+ // from using one map to the other. Use flag BPF_EXIST here since the map should
+ // be already populated in initMaps.
+ uint8_t newConfigure = (oldConfiguration.value() == SELECT_MAP_A) ? SELECT_MAP_B : SELECT_MAP_A;
+ auto res = mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, newConfigure,
+ BPF_EXIST);
+ if (!res.ok()) {
+ ALOGE("Failed to toggle the stats map: %s", strerror(res.error().code()));
+ return res;
+ }
+ // After changing the config, we need to make sure all the current running
+ // eBPF programs are finished and all the CPUs are aware of this config change
+ // before we modify the old map. So we do a special hack here to wait for
+ // the kernel to do a synchronize_rcu(). Once the kernel called
+ // synchronize_rcu(), the config we just updated will be available to all cores
+ // and the next eBPF programs triggered inside the kernel will use the new
+ // map configuration. So once this function returns we can safely modify the
+ // old stats map without concerning about race between the kernel and
+ // userspace.
+ int ret = synchronizeKernelRCU();
+ if (ret) {
+ ALOGE("map swap synchronize_rcu() ended with failure: %s", strerror(-ret));
+ return statusFromErrno(-ret, "map swap synchronize_rcu() failed");
+ }
+ return netdutils::status::ok;
+}
+
+void TrafficController::setPermissionForUids(int permission, const std::vector<uid_t>& uids) {
+ std::lock_guard guard(mMutex);
+ if (permission == INetd::PERMISSION_UNINSTALLED) {
+ for (uid_t uid : uids) {
+ // Clean up all permission information for the related uid if all the
+ // packages related to it are uninstalled.
+ mPrivilegedUser.erase(uid);
+ Status ret = mUidPermissionMap.deleteValue(uid);
+ if (!isOk(ret) && ret.code() != ENOENT) {
+ ALOGE("Failed to clean up the permission for %u: %s", uid, strerror(ret.code()));
+ }
+ }
+ return;
+ }
+
+ bool privileged = (permission & INetd::PERMISSION_UPDATE_DEVICE_STATS);
+
+ for (uid_t uid : uids) {
+ if (privileged) {
+ mPrivilegedUser.insert(uid);
+ } else {
+ mPrivilegedUser.erase(uid);
+ }
+
+ // The map stores all the permissions that the UID has, except if the only permission
+ // the UID has is the INTERNET permission, then the UID should not appear in the map.
+ if (permission != INetd::PERMISSION_INTERNET) {
+ Status ret = mUidPermissionMap.writeValue(uid, permission, BPF_ANY);
+ if (!isOk(ret)) {
+ ALOGE("Failed to set permission: %s of uid(%u) to permission map: %s",
+ UidPermissionTypeToString(permission).c_str(), uid, strerror(ret.code()));
+ }
+ } else {
+ Status ret = mUidPermissionMap.deleteValue(uid);
+ if (!isOk(ret) && ret.code() != ENOENT) {
+ ALOGE("Failed to remove uid %u from permission map: %s", uid, strerror(ret.code()));
+ }
+ }
+ }
+}
+
+std::string getProgramStatus(const char *path) {
+ int ret = access(path, R_OK);
+ if (ret == 0) {
+ return StringPrintf("OK");
+ }
+ if (ret != 0 && errno == ENOENT) {
+ return StringPrintf("program is missing at: %s", path);
+ }
+ return StringPrintf("check Program %s error: %s", path, strerror(errno));
+}
+
+std::string getMapStatus(const base::unique_fd& map_fd, const char* path) {
+ if (map_fd.get() < 0) {
+ return StringPrintf("map fd lost");
+ }
+ if (access(path, F_OK) != 0) {
+ return StringPrintf("map not pinned to location: %s", path);
+ }
+ return StringPrintf("OK");
+}
+
+// NOLINTNEXTLINE(google-runtime-references): grandfathered pass by non-const reference
+void dumpBpfMap(const std::string& mapName, DumpWriter& dw, const std::string& header) {
+ dw.blankline();
+ dw.println("%s:", mapName.c_str());
+ if (!header.empty()) {
+ dw.println(header);
+ }
+}
+
+const String16 TrafficController::DUMP_KEYWORD = String16("trafficcontroller");
+
+void TrafficController::dump(DumpWriter& dw, bool verbose) {
+ std::lock_guard guard(mMutex);
+ ScopedIndent indentTop(dw);
+ dw.println("TrafficController");
+
+ ScopedIndent indentPreBpfModule(dw);
+
+ dw.blankline();
+ dw.println("mCookieTagMap status: %s",
+ getMapStatus(mCookieTagMap.getMap(), COOKIE_TAG_MAP_PATH).c_str());
+ dw.println("mUidCounterSetMap status: %s",
+ getMapStatus(mUidCounterSetMap.getMap(), UID_COUNTERSET_MAP_PATH).c_str());
+ dw.println("mAppUidStatsMap status: %s",
+ getMapStatus(mAppUidStatsMap.getMap(), APP_UID_STATS_MAP_PATH).c_str());
+ dw.println("mStatsMapA status: %s",
+ getMapStatus(mStatsMapA.getMap(), STATS_MAP_A_PATH).c_str());
+ dw.println("mStatsMapB status: %s",
+ getMapStatus(mStatsMapB.getMap(), STATS_MAP_B_PATH).c_str());
+ dw.println("mIfaceIndexNameMap status: %s",
+ getMapStatus(mIfaceIndexNameMap.getMap(), IFACE_INDEX_NAME_MAP_PATH).c_str());
+ dw.println("mIfaceStatsMap status: %s",
+ getMapStatus(mIfaceStatsMap.getMap(), IFACE_STATS_MAP_PATH).c_str());
+ dw.println("mConfigurationMap status: %s",
+ getMapStatus(mConfigurationMap.getMap(), CONFIGURATION_MAP_PATH).c_str());
+ dw.println("mUidOwnerMap status: %s",
+ getMapStatus(mUidOwnerMap.getMap(), UID_OWNER_MAP_PATH).c_str());
+
+ dw.blankline();
+ dw.println("Cgroup ingress program status: %s",
+ getProgramStatus(BPF_INGRESS_PROG_PATH).c_str());
+ dw.println("Cgroup egress program status: %s", getProgramStatus(BPF_EGRESS_PROG_PATH).c_str());
+ dw.println("xt_bpf ingress program status: %s",
+ getProgramStatus(XT_BPF_INGRESS_PROG_PATH).c_str());
+ dw.println("xt_bpf egress program status: %s",
+ getProgramStatus(XT_BPF_EGRESS_PROG_PATH).c_str());
+ dw.println("xt_bpf bandwidth allowlist program status: %s",
+ getProgramStatus(XT_BPF_ALLOWLIST_PROG_PATH).c_str());
+ dw.println("xt_bpf bandwidth denylist program status: %s",
+ getProgramStatus(XT_BPF_DENYLIST_PROG_PATH).c_str());
+
+ if (!verbose) {
+ return;
+ }
+
+ dw.blankline();
+ dw.println("BPF map content:");
+
+ ScopedIndent indentForMapContent(dw);
+
+ // Print CookieTagMap content.
+ dumpBpfMap("mCookieTagMap", dw, "");
+ const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTagValue& value,
+ const BpfMap<uint64_t, UidTagValue>&) {
+ dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", key, value.tag, value.uid);
+ return base::Result<void>();
+ };
+ base::Result<void> res = mCookieTagMap.iterateWithValue(printCookieTagInfo);
+ if (!res.ok()) {
+ dw.println("mCookieTagMap print end with error: %s", res.error().message().c_str());
+ }
+
+ // Print UidCounterSetMap Content
+ dumpBpfMap("mUidCounterSetMap", dw, "");
+ const auto printUidInfo = [&dw](const uint32_t& key, const uint8_t& value,
+ const BpfMap<uint32_t, uint8_t>&) {
+ dw.println("%u %u", key, value);
+ return base::Result<void>();
+ };
+ res = mUidCounterSetMap.iterateWithValue(printUidInfo);
+ if (!res.ok()) {
+ dw.println("mUidCounterSetMap print end with error: %s", res.error().message().c_str());
+ }
+
+ // Print AppUidStatsMap content
+ std::string appUidStatsHeader = StringPrintf("uid rxBytes rxPackets txBytes txPackets");
+ dumpBpfMap("mAppUidStatsMap:", dw, appUidStatsHeader);
+ auto printAppUidStatsInfo = [&dw](const uint32_t& key, const StatsValue& value,
+ const BpfMap<uint32_t, StatsValue>&) {
+ dw.println("%u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, value.rxBytes,
+ value.rxPackets, value.txBytes, value.txPackets);
+ return base::Result<void>();
+ };
+ res = mAppUidStatsMap.iterateWithValue(printAppUidStatsInfo);
+ if (!res.ok()) {
+ dw.println("mAppUidStatsMap print end with error: %s", res.error().message().c_str());
+ }
+
+ // Print uidStatsMap content
+ std::string statsHeader = StringPrintf("ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes"
+ " rxPackets txBytes txPackets");
+ dumpBpfMap("mStatsMapA", dw, statsHeader);
+ const auto printStatsInfo = [&dw, this](const StatsKey& key, const StatsValue& value,
+ const BpfMap<StatsKey, StatsValue>&) {
+ uint32_t ifIndex = key.ifaceIndex;
+ auto ifname = mIfaceIndexNameMap.readValue(ifIndex);
+ if (!ifname.ok()) {
+ ifname = IfaceValue{"unknown"};
+ }
+ dw.println("%u %s 0x%x %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifIndex,
+ ifname.value().name, key.tag, key.uid, key.counterSet, value.rxBytes,
+ value.rxPackets, value.txBytes, value.txPackets);
+ return base::Result<void>();
+ };
+ res = mStatsMapA.iterateWithValue(printStatsInfo);
+ if (!res.ok()) {
+ dw.println("mStatsMapA print end with error: %s", res.error().message().c_str());
+ }
+
+ // Print TagStatsMap content.
+ dumpBpfMap("mStatsMapB", dw, statsHeader);
+ res = mStatsMapB.iterateWithValue(printStatsInfo);
+ if (!res.ok()) {
+ dw.println("mStatsMapB print end with error: %s", res.error().message().c_str());
+ }
+
+ // Print ifaceIndexToNameMap content.
+ dumpBpfMap("mIfaceIndexNameMap", dw, "");
+ const auto printIfaceNameInfo = [&dw](const uint32_t& key, const IfaceValue& value,
+ const BpfMap<uint32_t, IfaceValue>&) {
+ const char* ifname = value.name;
+ dw.println("ifaceIndex=%u ifaceName=%s", key, ifname);
+ return base::Result<void>();
+ };
+ res = mIfaceIndexNameMap.iterateWithValue(printIfaceNameInfo);
+ if (!res.ok()) {
+ dw.println("mIfaceIndexNameMap print end with error: %s", res.error().message().c_str());
+ }
+
+ // Print ifaceStatsMap content
+ std::string ifaceStatsHeader = StringPrintf("ifaceIndex ifaceName rxBytes rxPackets txBytes"
+ " txPackets");
+ dumpBpfMap("mIfaceStatsMap:", dw, ifaceStatsHeader);
+ const auto printIfaceStatsInfo = [&dw, this](const uint32_t& key, const StatsValue& value,
+ const BpfMap<uint32_t, StatsValue>&) {
+ auto ifname = mIfaceIndexNameMap.readValue(key);
+ if (!ifname.ok()) {
+ ifname = IfaceValue{"unknown"};
+ }
+ dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, ifname.value().name,
+ value.rxBytes, value.rxPackets, value.txBytes, value.txPackets);
+ return base::Result<void>();
+ };
+ res = mIfaceStatsMap.iterateWithValue(printIfaceStatsInfo);
+ if (!res.ok()) {
+ dw.println("mIfaceStatsMap print end with error: %s", res.error().message().c_str());
+ }
+
+ dw.blankline();
+
+ uint32_t key = UID_RULES_CONFIGURATION_KEY;
+ auto configuration = mConfigurationMap.readValue(key);
+ if (configuration.ok()) {
+ dw.println("current ownerMatch configuration: %d%s", configuration.value(),
+ uidMatchTypeToString(configuration.value()).c_str());
+ } else {
+ dw.println("mConfigurationMap read ownerMatch configure failed with error: %s",
+ configuration.error().message().c_str());
+ }
+
+ key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
+ configuration = mConfigurationMap.readValue(key);
+ if (configuration.ok()) {
+ const char* statsMapDescription = "???";
+ switch (configuration.value()) {
+ case SELECT_MAP_A:
+ statsMapDescription = "SELECT_MAP_A";
+ break;
+ case SELECT_MAP_B:
+ statsMapDescription = "SELECT_MAP_B";
+ break;
+ // No default clause, so if we ever add a third map, this code will fail to build.
+ }
+ dw.println("current statsMap configuration: %d %s", configuration.value(),
+ statsMapDescription);
+ } else {
+ dw.println("mConfigurationMap read stats map configure failed with error: %s",
+ configuration.error().message().c_str());
+ }
+ dumpBpfMap("mUidOwnerMap", dw, "");
+ const auto printUidMatchInfo = [&dw, this](const uint32_t& key, const UidOwnerValue& value,
+ const BpfMap<uint32_t, UidOwnerValue>&) {
+ if (value.rule & IIF_MATCH) {
+ auto ifname = mIfaceIndexNameMap.readValue(value.iif);
+ if (ifname.ok()) {
+ dw.println("%u %s %s", key, uidMatchTypeToString(value.rule).c_str(),
+ ifname.value().name);
+ } else {
+ dw.println("%u %s %u", key, uidMatchTypeToString(value.rule).c_str(), value.iif);
+ }
+ } else {
+ dw.println("%u %s", key, uidMatchTypeToString(value.rule).c_str());
+ }
+ return base::Result<void>();
+ };
+ res = mUidOwnerMap.iterateWithValue(printUidMatchInfo);
+ if (!res.ok()) {
+ dw.println("mUidOwnerMap print end with error: %s", res.error().message().c_str());
+ }
+ dumpBpfMap("mUidPermissionMap", dw, "");
+ const auto printUidPermissionInfo = [&dw](const uint32_t& key, const int& value,
+ const BpfMap<uint32_t, uint8_t>&) {
+ dw.println("%u %s", key, UidPermissionTypeToString(value).c_str());
+ return base::Result<void>();
+ };
+ res = mUidPermissionMap.iterateWithValue(printUidPermissionInfo);
+ if (!res.ok()) {
+ dw.println("mUidPermissionMap print end with error: %s", res.error().message().c_str());
+ }
+
+ dumpBpfMap("mPrivilegedUser", dw, "");
+ for (uid_t uid : mPrivilegedUser) {
+ dw.println("%u ALLOW_UPDATE_DEVICE_STATS", (uint32_t)uid);
+ }
+}
+
+} // namespace net
+} // namespace android
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
new file mode 100644
index 0000000..f5d5911
--- /dev/null
+++ b/service/native/TrafficControllerTest.cpp
@@ -0,0 +1,741 @@
+/*
+ * Copyright 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.
+ *
+ * TrafficControllerTest.cpp - unit tests for TrafficController.cpp
+ */
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/inet_diag.h>
+#include <linux/sock_diag.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <netdutils/MockSyscalls.h>
+
+#include "TrafficController.h"
+#include "bpf/BpfUtils.h"
+
+using namespace android::bpf; // NOLINT(google-build-using-namespace): grandfathered
+
+namespace android {
+namespace net {
+
+using base::Result;
+using netdutils::isOk;
+
+constexpr int TEST_MAP_SIZE = 10;
+constexpr uid_t TEST_UID = 10086;
+constexpr uid_t TEST_UID2 = 54321;
+constexpr uid_t TEST_UID3 = 98765;
+constexpr uint32_t TEST_TAG = 42;
+constexpr uint32_t TEST_COUNTERSET = 1;
+constexpr uint32_t DEFAULT_COUNTERSET = 0;
+
+#define ASSERT_VALID(x) ASSERT_TRUE((x).isValid())
+
+class TrafficControllerTest : public ::testing::Test {
+ protected:
+ TrafficControllerTest() {}
+ TrafficController mTc;
+ BpfMap<uint64_t, UidTagValue> mFakeCookieTagMap;
+ BpfMap<uint32_t, uint8_t> mFakeUidCounterSetMap;
+ BpfMap<uint32_t, StatsValue> mFakeAppUidStatsMap;
+ BpfMap<StatsKey, StatsValue> mFakeStatsMapA;
+ BpfMap<uint32_t, uint8_t> mFakeConfigurationMap;
+ BpfMap<uint32_t, UidOwnerValue> mFakeUidOwnerMap;
+ BpfMap<uint32_t, uint8_t> mFakeUidPermissionMap;
+
+ void SetUp() {
+ std::lock_guard guard(mTc.mMutex);
+ ASSERT_EQ(0, setrlimitForTest());
+
+ mFakeCookieTagMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint64_t), sizeof(UidTagValue),
+ TEST_MAP_SIZE, 0));
+ ASSERT_VALID(mFakeCookieTagMap);
+
+ mFakeUidCounterSetMap.reset(
+ createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint8_t), TEST_MAP_SIZE, 0));
+ ASSERT_VALID(mFakeUidCounterSetMap);
+
+ mFakeAppUidStatsMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(StatsValue),
+ TEST_MAP_SIZE, 0));
+ ASSERT_VALID(mFakeAppUidStatsMap);
+
+ mFakeStatsMapA.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(StatsKey), sizeof(StatsValue),
+ TEST_MAP_SIZE, 0));
+ ASSERT_VALID(mFakeStatsMapA);
+
+ mFakeConfigurationMap.reset(
+ createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint8_t), 1, 0));
+ ASSERT_VALID(mFakeConfigurationMap);
+
+ mFakeUidOwnerMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(UidOwnerValue),
+ TEST_MAP_SIZE, 0));
+ ASSERT_VALID(mFakeUidOwnerMap);
+ mFakeUidPermissionMap.reset(
+ createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint8_t), TEST_MAP_SIZE, 0));
+ ASSERT_VALID(mFakeUidPermissionMap);
+
+ mTc.mCookieTagMap.reset(dupFd(mFakeCookieTagMap.getMap()));
+ ASSERT_VALID(mTc.mCookieTagMap);
+ mTc.mUidCounterSetMap.reset(dupFd(mFakeUidCounterSetMap.getMap()));
+ ASSERT_VALID(mTc.mUidCounterSetMap);
+ mTc.mAppUidStatsMap.reset(dupFd(mFakeAppUidStatsMap.getMap()));
+ ASSERT_VALID(mTc.mAppUidStatsMap);
+ mTc.mStatsMapA.reset(dupFd(mFakeStatsMapA.getMap()));
+ ASSERT_VALID(mTc.mStatsMapA);
+ mTc.mConfigurationMap.reset(dupFd(mFakeConfigurationMap.getMap()));
+ ASSERT_VALID(mTc.mConfigurationMap);
+
+ // Always write to stats map A by default.
+ ASSERT_RESULT_OK(mTc.mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY,
+ SELECT_MAP_A, BPF_ANY));
+ mTc.mUidOwnerMap.reset(dupFd(mFakeUidOwnerMap.getMap()));
+ ASSERT_VALID(mTc.mUidOwnerMap);
+ mTc.mUidPermissionMap.reset(dupFd(mFakeUidPermissionMap.getMap()));
+ ASSERT_VALID(mTc.mUidPermissionMap);
+ mTc.mPrivilegedUser.clear();
+ }
+
+ int dupFd(const android::base::unique_fd& mapFd) {
+ return fcntl(mapFd.get(), F_DUPFD_CLOEXEC, 0);
+ }
+
+ void populateFakeStats(uint64_t cookie, uint32_t uid, uint32_t tag, StatsKey* key) {
+ UidTagValue cookieMapkey = {.uid = (uint32_t)uid, .tag = tag};
+ EXPECT_RESULT_OK(mFakeCookieTagMap.writeValue(cookie, cookieMapkey, BPF_ANY));
+ *key = {.uid = uid, .tag = tag, .counterSet = TEST_COUNTERSET, .ifaceIndex = 1};
+ StatsValue statsMapValue = {.rxPackets = 1, .rxBytes = 100};
+ uint8_t counterSet = TEST_COUNTERSET;
+ EXPECT_RESULT_OK(mFakeUidCounterSetMap.writeValue(uid, counterSet, BPF_ANY));
+ EXPECT_RESULT_OK(mFakeStatsMapA.writeValue(*key, statsMapValue, BPF_ANY));
+ key->tag = 0;
+ EXPECT_RESULT_OK(mFakeStatsMapA.writeValue(*key, statsMapValue, BPF_ANY));
+ EXPECT_RESULT_OK(mFakeAppUidStatsMap.writeValue(uid, statsMapValue, BPF_ANY));
+ // put tag information back to statsKey
+ key->tag = tag;
+ }
+
+ void checkUidOwnerRuleForChain(ChildChain chain, UidOwnerMatchType match) {
+ uint32_t uid = TEST_UID;
+ EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, DENYLIST));
+ Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
+ EXPECT_TRUE(value.value().rule & match);
+
+ uid = TEST_UID2;
+ EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, ALLOWLIST));
+ value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
+ EXPECT_TRUE(value.value().rule & match);
+
+ EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, ALLOWLIST));
+ value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_FALSE(value.ok());
+ EXPECT_EQ(ENOENT, value.error().code());
+
+ uid = TEST_UID;
+ EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, DENYLIST));
+ value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_FALSE(value.ok());
+ EXPECT_EQ(ENOENT, value.error().code());
+
+ uid = TEST_UID3;
+ EXPECT_EQ(-ENOENT, mTc.changeUidOwnerRule(chain, uid, ALLOW, DENYLIST));
+ value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_FALSE(value.ok());
+ EXPECT_EQ(ENOENT, value.error().code());
+ }
+
+ void checkEachUidValue(const std::vector<int32_t>& uids, UidOwnerMatchType match) {
+ for (uint32_t uid : uids) {
+ Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
+ EXPECT_TRUE(value.value().rule & match);
+ }
+ std::set<uint32_t> uidSet(uids.begin(), uids.end());
+ const auto checkNoOtherUid = [&uidSet](const int32_t& key,
+ const BpfMap<uint32_t, UidOwnerValue>&) {
+ EXPECT_NE(uidSet.end(), uidSet.find(key));
+ return Result<void>();
+ };
+ EXPECT_RESULT_OK(mFakeUidOwnerMap.iterate(checkNoOtherUid));
+ }
+
+ void checkUidMapReplace(const std::string& name, const std::vector<int32_t>& uids,
+ UidOwnerMatchType match) {
+ bool isAllowlist = true;
+ EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isAllowlist, uids));
+ checkEachUidValue(uids, match);
+
+ isAllowlist = false;
+ EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isAllowlist, uids));
+ checkEachUidValue(uids, match);
+ }
+
+ void expectUidOwnerMapValues(const std::vector<uint32_t>& appUids, uint8_t expectedRule,
+ uint32_t expectedIif) {
+ for (uint32_t uid : appUids) {
+ Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
+ EXPECT_EQ(expectedRule, value.value().rule)
+ << "Expected rule for UID " << uid << " to be " << expectedRule << ", but was "
+ << value.value().rule;
+ EXPECT_EQ(expectedIif, value.value().iif)
+ << "Expected iif for UID " << uid << " to be " << expectedIif << ", but was "
+ << value.value().iif;
+ }
+ }
+
+ template <class Key, class Value>
+ void expectMapEmpty(BpfMap<Key, Value>& map) {
+ auto isEmpty = map.isEmpty();
+ EXPECT_RESULT_OK(isEmpty);
+ EXPECT_TRUE(isEmpty.value());
+ }
+
+ void expectUidPermissionMapValues(const std::vector<uid_t>& appUids, uint8_t expectedValue) {
+ for (uid_t uid : appUids) {
+ Result<uint8_t> value = mFakeUidPermissionMap.readValue(uid);
+ EXPECT_RESULT_OK(value);
+ EXPECT_EQ(expectedValue, value.value())
+ << "Expected value for UID " << uid << " to be " << expectedValue
+ << ", but was " << value.value();
+ }
+ }
+
+ void expectPrivilegedUserSet(const std::vector<uid_t>& appUids) {
+ std::lock_guard guard(mTc.mMutex);
+ EXPECT_EQ(appUids.size(), mTc.mPrivilegedUser.size());
+ for (uid_t uid : appUids) {
+ EXPECT_NE(mTc.mPrivilegedUser.end(), mTc.mPrivilegedUser.find(uid));
+ }
+ }
+
+ void expectPrivilegedUserSetEmpty() {
+ std::lock_guard guard(mTc.mMutex);
+ EXPECT_TRUE(mTc.mPrivilegedUser.empty());
+ }
+
+ void addPrivilegedUid(uid_t uid) {
+ std::vector privilegedUid = {uid};
+ mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, privilegedUid);
+ }
+
+ void removePrivilegedUid(uid_t uid) {
+ std::vector privilegedUid = {uid};
+ mTc.setPermissionForUids(INetd::PERMISSION_NONE, privilegedUid);
+ }
+
+ void expectFakeStatsUnchanged(uint64_t cookie, uint32_t tag, uint32_t uid,
+ StatsKey tagStatsMapKey) {
+ Result<UidTagValue> cookieMapResult = mFakeCookieTagMap.readValue(cookie);
+ EXPECT_RESULT_OK(cookieMapResult);
+ EXPECT_EQ(uid, cookieMapResult.value().uid);
+ EXPECT_EQ(tag, cookieMapResult.value().tag);
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ EXPECT_RESULT_OK(counterSetResult);
+ EXPECT_EQ(TEST_COUNTERSET, counterSetResult.value());
+ Result<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey);
+ EXPECT_RESULT_OK(statsMapResult);
+ EXPECT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
+ EXPECT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
+ tagStatsMapKey.tag = 0;
+ statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey);
+ EXPECT_RESULT_OK(statsMapResult);
+ EXPECT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
+ EXPECT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
+ auto appStatsResult = mFakeAppUidStatsMap.readValue(uid);
+ EXPECT_RESULT_OK(appStatsResult);
+ EXPECT_EQ((uint64_t)1, appStatsResult.value().rxPackets);
+ EXPECT_EQ((uint64_t)100, appStatsResult.value().rxBytes);
+ }
+};
+
+TEST_F(TrafficControllerTest, TestSetCounterSet) {
+ uid_t callingUid = TEST_UID2;
+ addPrivilegedUid(callingUid);
+ ASSERT_EQ(0, mTc.setCounterSet(TEST_COUNTERSET, TEST_UID, callingUid));
+ uid_t uid = TEST_UID;
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_RESULT_OK(counterSetResult);
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
+ ASSERT_EQ(0, mTc.setCounterSet(DEFAULT_COUNTERSET, TEST_UID, callingUid));
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok());
+ expectMapEmpty(mFakeUidCounterSetMap);
+}
+
+TEST_F(TrafficControllerTest, TestSetCounterSetWithoutPermission) {
+ ASSERT_EQ(-EPERM, mTc.setCounterSet(TEST_COUNTERSET, TEST_UID, TEST_UID2));
+ uid_t uid = TEST_UID;
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok());
+ expectMapEmpty(mFakeUidCounterSetMap);
+}
+
+TEST_F(TrafficControllerTest, TestSetInvalidCounterSet) {
+ uid_t callingUid = TEST_UID2;
+ addPrivilegedUid(callingUid);
+ ASSERT_GT(0, mTc.setCounterSet(OVERFLOW_COUNTERSET, TEST_UID, callingUid));
+ uid_t uid = TEST_UID;
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok());
+ expectMapEmpty(mFakeUidCounterSetMap);
+}
+
+TEST_F(TrafficControllerTest, TestDeleteTagDataWithoutPermission) {
+ uint64_t cookie = 1;
+ uid_t uid = TEST_UID;
+ uint32_t tag = TEST_TAG;
+ StatsKey tagStatsMapKey;
+ populateFakeStats(cookie, uid, tag, &tagStatsMapKey);
+ ASSERT_EQ(-EPERM, mTc.deleteTagData(0, TEST_UID, TEST_UID2));
+
+ expectFakeStatsUnchanged(cookie, tag, uid, tagStatsMapKey);
+}
+
+TEST_F(TrafficControllerTest, TestDeleteTagData) {
+ uid_t callingUid = TEST_UID2;
+ addPrivilegedUid(callingUid);
+ uint64_t cookie = 1;
+ uid_t uid = TEST_UID;
+ uint32_t tag = TEST_TAG;
+ StatsKey tagStatsMapKey;
+ populateFakeStats(cookie, uid, tag, &tagStatsMapKey);
+ ASSERT_EQ(0, mTc.deleteTagData(TEST_TAG, TEST_UID, callingUid));
+ ASSERT_FALSE(mFakeCookieTagMap.readValue(cookie).ok());
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_RESULT_OK(counterSetResult);
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey).ok());
+ tagStatsMapKey.tag = 0;
+ Result<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey);
+ ASSERT_RESULT_OK(statsMapResult);
+ ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
+ auto appStatsResult = mFakeAppUidStatsMap.readValue(TEST_UID);
+ ASSERT_RESULT_OK(appStatsResult);
+ ASSERT_EQ((uint64_t)1, appStatsResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, appStatsResult.value().rxBytes);
+}
+
+TEST_F(TrafficControllerTest, TestDeleteAllUidData) {
+ uid_t callingUid = TEST_UID2;
+ addPrivilegedUid(callingUid);
+ uint64_t cookie = 1;
+ uid_t uid = TEST_UID;
+ uint32_t tag = TEST_TAG;
+ StatsKey tagStatsMapKey;
+ populateFakeStats(cookie, uid, tag, &tagStatsMapKey);
+ ASSERT_EQ(0, mTc.deleteTagData(0, TEST_UID, callingUid));
+ ASSERT_FALSE(mFakeCookieTagMap.readValue(cookie).ok());
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid).ok());
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey).ok());
+ tagStatsMapKey.tag = 0;
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey).ok());
+ ASSERT_FALSE(mFakeAppUidStatsMap.readValue(TEST_UID).ok());
+}
+
+TEST_F(TrafficControllerTest, TestDeleteDataWithTwoTags) {
+ uid_t callingUid = TEST_UID2;
+ addPrivilegedUid(callingUid);
+ uint64_t cookie1 = 1;
+ uint64_t cookie2 = 2;
+ uid_t uid = TEST_UID;
+ uint32_t tag1 = TEST_TAG;
+ uint32_t tag2 = TEST_TAG + 1;
+ StatsKey tagStatsMapKey1;
+ StatsKey tagStatsMapKey2;
+ populateFakeStats(cookie1, uid, tag1, &tagStatsMapKey1);
+ populateFakeStats(cookie2, uid, tag2, &tagStatsMapKey2);
+ ASSERT_EQ(0, mTc.deleteTagData(TEST_TAG, TEST_UID, callingUid));
+ ASSERT_FALSE(mFakeCookieTagMap.readValue(cookie1).ok());
+ Result<UidTagValue> cookieMapResult = mFakeCookieTagMap.readValue(cookie2);
+ ASSERT_RESULT_OK(cookieMapResult);
+ ASSERT_EQ(TEST_UID, cookieMapResult.value().uid);
+ ASSERT_EQ(TEST_TAG + 1, cookieMapResult.value().tag);
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_RESULT_OK(counterSetResult);
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey1).ok());
+ Result<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey2);
+ ASSERT_RESULT_OK(statsMapResult);
+ ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
+}
+
+TEST_F(TrafficControllerTest, TestDeleteDataWithTwoUids) {
+ uid_t callingUid = TEST_UID2;
+ addPrivilegedUid(callingUid);
+ uint64_t cookie1 = 1;
+ uint64_t cookie2 = 2;
+ uid_t uid1 = TEST_UID;
+ uid_t uid2 = TEST_UID + 1;
+ uint32_t tag = TEST_TAG;
+ StatsKey tagStatsMapKey1;
+ StatsKey tagStatsMapKey2;
+ populateFakeStats(cookie1, uid1, tag, &tagStatsMapKey1);
+ populateFakeStats(cookie2, uid2, tag, &tagStatsMapKey2);
+
+ // Delete the stats of one of the uid. Check if it is properly collected by
+ // removedStats.
+ ASSERT_EQ(0, mTc.deleteTagData(0, uid2, callingUid));
+ ASSERT_FALSE(mFakeCookieTagMap.readValue(cookie2).ok());
+ Result<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid1);
+ ASSERT_RESULT_OK(counterSetResult);
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
+ ASSERT_FALSE(mFakeUidCounterSetMap.readValue(uid2).ok());
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey2).ok());
+ tagStatsMapKey2.tag = 0;
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey2).ok());
+ ASSERT_FALSE(mFakeAppUidStatsMap.readValue(uid2).ok());
+ tagStatsMapKey1.tag = 0;
+ Result<StatsValue> statsMapResult = mFakeStatsMapA.readValue(tagStatsMapKey1);
+ ASSERT_RESULT_OK(statsMapResult);
+ ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
+ auto appStatsResult = mFakeAppUidStatsMap.readValue(uid1);
+ ASSERT_RESULT_OK(appStatsResult);
+ ASSERT_EQ((uint64_t)1, appStatsResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, appStatsResult.value().rxBytes);
+
+ // Delete the stats of the other uid.
+ ASSERT_EQ(0, mTc.deleteTagData(0, uid1, callingUid));
+ ASSERT_FALSE(mFakeStatsMapA.readValue(tagStatsMapKey1).ok());
+ ASSERT_FALSE(mFakeAppUidStatsMap.readValue(uid1).ok());
+}
+
+TEST_F(TrafficControllerTest, TestUpdateOwnerMapEntry) {
+ uint32_t uid = TEST_UID;
+ ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, DENY, DENYLIST)));
+ Result<UidOwnerValue> value = mFakeUidOwnerMap.readValue(uid);
+ ASSERT_RESULT_OK(value);
+ ASSERT_TRUE(value.value().rule & STANDBY_MATCH);
+
+ ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, ALLOW, ALLOWLIST)));
+ value = mFakeUidOwnerMap.readValue(uid);
+ ASSERT_RESULT_OK(value);
+ ASSERT_TRUE(value.value().rule & DOZABLE_MATCH);
+
+ ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(DOZABLE_MATCH, uid, DENY, ALLOWLIST)));
+ value = mFakeUidOwnerMap.readValue(uid);
+ ASSERT_RESULT_OK(value);
+ ASSERT_FALSE(value.value().rule & DOZABLE_MATCH);
+
+ ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, DENYLIST)));
+ ASSERT_FALSE(mFakeUidOwnerMap.readValue(uid).ok());
+
+ uid = TEST_UID2;
+ ASSERT_FALSE(isOk(mTc.updateOwnerMapEntry(STANDBY_MATCH, uid, ALLOW, DENYLIST)));
+ ASSERT_FALSE(mFakeUidOwnerMap.readValue(uid).ok());
+}
+
+TEST_F(TrafficControllerTest, TestChangeUidOwnerRule) {
+ checkUidOwnerRuleForChain(DOZABLE, DOZABLE_MATCH);
+ checkUidOwnerRuleForChain(STANDBY, STANDBY_MATCH);
+ checkUidOwnerRuleForChain(POWERSAVE, POWERSAVE_MATCH);
+ checkUidOwnerRuleForChain(RESTRICTED, RESTRICTED_MATCH);
+ ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(NONE, TEST_UID, ALLOW, ALLOWLIST));
+ ASSERT_EQ(-EINVAL, mTc.changeUidOwnerRule(INVALID_CHAIN, TEST_UID, ALLOW, ALLOWLIST));
+}
+
+TEST_F(TrafficControllerTest, TestReplaceUidOwnerMap) {
+ std::vector<int32_t> uids = {TEST_UID, TEST_UID2, TEST_UID3};
+ checkUidMapReplace("fw_dozable", uids, DOZABLE_MATCH);
+ checkUidMapReplace("fw_standby", uids, STANDBY_MATCH);
+ checkUidMapReplace("fw_powersave", uids, POWERSAVE_MATCH);
+ checkUidMapReplace("fw_restricted", uids, RESTRICTED_MATCH);
+ ASSERT_EQ(-EINVAL, mTc.replaceUidOwnerMap("unknow", true, uids));
+}
+
+TEST_F(TrafficControllerTest, TestReplaceSameChain) {
+ std::vector<int32_t> uids = {TEST_UID, TEST_UID2, TEST_UID3};
+ checkUidMapReplace("fw_dozable", uids, DOZABLE_MATCH);
+ std::vector<int32_t> newUids = {TEST_UID2, TEST_UID3};
+ checkUidMapReplace("fw_dozable", newUids, DOZABLE_MATCH);
+}
+
+TEST_F(TrafficControllerTest, TestDenylistUidMatch) {
+ std::vector<uint32_t> appUids = {1000, 1001, 10012};
+ ASSERT_TRUE(isOk(
+ mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, TrafficController::IptOpInsert)));
+ expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0);
+ ASSERT_TRUE(isOk(
+ mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, TrafficController::IptOpDelete)));
+ expectMapEmpty(mFakeUidOwnerMap);
+}
+
+TEST_F(TrafficControllerTest, TestAllowlistUidMatch) {
+ std::vector<uint32_t> appUids = {1000, 1001, 10012};
+ ASSERT_TRUE(
+ isOk(mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpInsert)));
+ expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0);
+ ASSERT_TRUE(
+ isOk(mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpDelete)));
+ expectMapEmpty(mFakeUidOwnerMap);
+}
+
+TEST_F(TrafficControllerTest, TestReplaceMatchUid) {
+ std::vector<uint32_t> appUids = {1000, 1001, 10012};
+ // Add appUids to the denylist and expect that their values are all PENALTY_BOX_MATCH.
+ ASSERT_TRUE(isOk(
+ mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, TrafficController::IptOpInsert)));
+ expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0);
+
+ // Add the same UIDs to the allowlist and expect that we get PENALTY_BOX_MATCH |
+ // HAPPY_BOX_MATCH.
+ ASSERT_TRUE(
+ isOk(mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpInsert)));
+ expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH | PENALTY_BOX_MATCH, 0);
+
+ // Remove the same UIDs from the allowlist and check the PENALTY_BOX_MATCH is still there.
+ ASSERT_TRUE(
+ isOk(mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpDelete)));
+ expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0);
+
+ // Remove the same UIDs from the denylist and check the map is empty.
+ ASSERT_TRUE(isOk(
+ mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, TrafficController::IptOpDelete)));
+ ASSERT_FALSE(mFakeUidOwnerMap.getFirstKey().ok());
+}
+
+TEST_F(TrafficControllerTest, TestDeleteWrongMatchSilentlyFails) {
+ std::vector<uint32_t> appUids = {1000, 1001, 10012};
+ // If the uid does not exist in the map, trying to delete a rule about it will fail.
+ ASSERT_FALSE(
+ isOk(mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpDelete)));
+ expectMapEmpty(mFakeUidOwnerMap);
+
+ // Add denylist rules for appUids.
+ ASSERT_TRUE(
+ isOk(mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpInsert)));
+ expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0);
+
+ // Delete (non-existent) denylist rules for appUids, and check that this silently does
+ // nothing if the uid is in the map but does not have denylist match. This is required because
+ // NetworkManagementService will try to remove a uid from denylist after adding it to the
+ // allowlist and if the remove fails it will not update the uid status.
+ ASSERT_TRUE(isOk(
+ mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH, TrafficController::IptOpDelete)));
+ expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0);
+}
+
+TEST_F(TrafficControllerTest, TestAddUidInterfaceFilteringRules) {
+ int iif0 = 15;
+ ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif0, {1000, 1001})));
+ expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0);
+
+ // Add some non-overlapping new uids. They should coexist with existing rules
+ int iif1 = 16;
+ ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {2000, 2001})));
+ expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0);
+ expectUidOwnerMapValues({2000, 2001}, IIF_MATCH, iif1);
+
+ // Overwrite some existing uids
+ int iif2 = 17;
+ ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif2, {1000, 2000})));
+ expectUidOwnerMapValues({1001}, IIF_MATCH, iif0);
+ expectUidOwnerMapValues({2001}, IIF_MATCH, iif1);
+ expectUidOwnerMapValues({1000, 2000}, IIF_MATCH, iif2);
+}
+
+TEST_F(TrafficControllerTest, TestRemoveUidInterfaceFilteringRules) {
+ int iif0 = 15;
+ int iif1 = 16;
+ ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif0, {1000, 1001})));
+ ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {2000, 2001})));
+ expectUidOwnerMapValues({1000, 1001}, IIF_MATCH, iif0);
+ expectUidOwnerMapValues({2000, 2001}, IIF_MATCH, iif1);
+
+ // Rmove some uids
+ ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({1001, 2001})));
+ expectUidOwnerMapValues({1000}, IIF_MATCH, iif0);
+ expectUidOwnerMapValues({2000}, IIF_MATCH, iif1);
+ checkEachUidValue({1000, 2000}, IIF_MATCH); // Make sure there are only two uids remaining
+
+ // Remove non-existent uids shouldn't fail
+ ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({2000, 3000})));
+ expectUidOwnerMapValues({1000}, IIF_MATCH, iif0);
+ checkEachUidValue({1000}, IIF_MATCH); // Make sure there are only one uid remaining
+
+ // Remove everything
+ ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({1000})));
+ expectMapEmpty(mFakeUidOwnerMap);
+}
+
+TEST_F(TrafficControllerTest, TestUidInterfaceFilteringRulesCoexistWithExistingMatches) {
+ // Set up existing PENALTY_BOX_MATCH rules
+ ASSERT_TRUE(isOk(mTc.updateUidOwnerMap({1000, 1001, 10012}, PENALTY_BOX_MATCH,
+ TrafficController::IptOpInsert)));
+ expectUidOwnerMapValues({1000, 1001, 10012}, PENALTY_BOX_MATCH, 0);
+
+ // Add some partially-overlapping uid owner rules and check result
+ int iif1 = 32;
+ ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {10012, 10013, 10014})));
+ expectUidOwnerMapValues({1000, 1001}, PENALTY_BOX_MATCH, 0);
+ expectUidOwnerMapValues({10012}, PENALTY_BOX_MATCH | IIF_MATCH, iif1);
+ expectUidOwnerMapValues({10013, 10014}, IIF_MATCH, iif1);
+
+ // Removing some PENALTY_BOX_MATCH rules should not change uid interface rule
+ ASSERT_TRUE(isOk(mTc.updateUidOwnerMap({1001, 10012}, PENALTY_BOX_MATCH,
+ TrafficController::IptOpDelete)));
+ expectUidOwnerMapValues({1000}, PENALTY_BOX_MATCH, 0);
+ expectUidOwnerMapValues({10012, 10013, 10014}, IIF_MATCH, iif1);
+
+ // Remove all uid interface rules
+ ASSERT_TRUE(isOk(mTc.removeUidInterfaceRules({10012, 10013, 10014})));
+ expectUidOwnerMapValues({1000}, PENALTY_BOX_MATCH, 0);
+ // Make sure these are the only uids left
+ checkEachUidValue({1000}, PENALTY_BOX_MATCH);
+}
+
+TEST_F(TrafficControllerTest, TestUidInterfaceFilteringRulesCoexistWithNewMatches) {
+ int iif1 = 56;
+ // Set up existing uid interface rules
+ ASSERT_TRUE(isOk(mTc.addUidInterfaceRules(iif1, {10001, 10002})));
+ expectUidOwnerMapValues({10001, 10002}, IIF_MATCH, iif1);
+
+ // Add some partially-overlapping doze rules
+ EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_dozable", true, {10002, 10003}));
+ expectUidOwnerMapValues({10001}, IIF_MATCH, iif1);
+ expectUidOwnerMapValues({10002}, DOZABLE_MATCH | IIF_MATCH, iif1);
+ expectUidOwnerMapValues({10003}, DOZABLE_MATCH, 0);
+
+ // Introduce a third rule type (powersave) on various existing UIDs
+ EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_powersave", true, {10000, 10001, 10002, 10003}));
+ expectUidOwnerMapValues({10000}, POWERSAVE_MATCH, 0);
+ expectUidOwnerMapValues({10001}, POWERSAVE_MATCH | IIF_MATCH, iif1);
+ expectUidOwnerMapValues({10002}, POWERSAVE_MATCH | DOZABLE_MATCH | IIF_MATCH, iif1);
+ expectUidOwnerMapValues({10003}, POWERSAVE_MATCH | DOZABLE_MATCH, 0);
+
+ // Remove all doze rules
+ EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_dozable", true, {}));
+ expectUidOwnerMapValues({10000}, POWERSAVE_MATCH, 0);
+ expectUidOwnerMapValues({10001}, POWERSAVE_MATCH | IIF_MATCH, iif1);
+ expectUidOwnerMapValues({10002}, POWERSAVE_MATCH | IIF_MATCH, iif1);
+ expectUidOwnerMapValues({10003}, POWERSAVE_MATCH, 0);
+
+ // Remove all powersave rules, expect ownerMap to only have uid interface rules left
+ EXPECT_EQ(0, mTc.replaceUidOwnerMap("fw_powersave", true, {}));
+ expectUidOwnerMapValues({10001, 10002}, IIF_MATCH, iif1);
+ // Make sure these are the only uids left
+ checkEachUidValue({10001, 10002}, IIF_MATCH);
+}
+
+TEST_F(TrafficControllerTest, TestGrantInternetPermission) {
+ std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3};
+
+ mTc.setPermissionForUids(INetd::PERMISSION_INTERNET, appUids);
+ expectMapEmpty(mFakeUidPermissionMap);
+ expectPrivilegedUserSetEmpty();
+}
+
+TEST_F(TrafficControllerTest, TestRevokeInternetPermission) {
+ std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3};
+
+ mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids);
+ expectUidPermissionMapValues(appUids, INetd::PERMISSION_NONE);
+}
+
+TEST_F(TrafficControllerTest, TestPermissionUninstalled) {
+ std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3};
+
+ mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids);
+ expectUidPermissionMapValues(appUids, INetd::PERMISSION_UPDATE_DEVICE_STATS);
+ expectPrivilegedUserSet(appUids);
+
+ std::vector<uid_t> uidToRemove = {TEST_UID};
+ mTc.setPermissionForUids(INetd::PERMISSION_UNINSTALLED, uidToRemove);
+
+ std::vector<uid_t> uidRemain = {TEST_UID3, TEST_UID2};
+ expectUidPermissionMapValues(uidRemain, INetd::PERMISSION_UPDATE_DEVICE_STATS);
+ expectPrivilegedUserSet(uidRemain);
+
+ mTc.setPermissionForUids(INetd::PERMISSION_UNINSTALLED, uidRemain);
+ expectMapEmpty(mFakeUidPermissionMap);
+ expectPrivilegedUserSetEmpty();
+}
+
+TEST_F(TrafficControllerTest, TestGrantUpdateStatsPermission) {
+ std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3};
+
+ mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids);
+ expectUidPermissionMapValues(appUids, INetd::PERMISSION_UPDATE_DEVICE_STATS);
+ expectPrivilegedUserSet(appUids);
+
+ mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids);
+ expectPrivilegedUserSetEmpty();
+ expectUidPermissionMapValues(appUids, INetd::PERMISSION_NONE);
+}
+
+TEST_F(TrafficControllerTest, TestRevokeUpdateStatsPermission) {
+ std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3};
+
+ mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids);
+ expectPrivilegedUserSet(appUids);
+
+ std::vector<uid_t> uidToRemove = {TEST_UID};
+ mTc.setPermissionForUids(INetd::PERMISSION_NONE, uidToRemove);
+
+ std::vector<uid_t> uidRemain = {TEST_UID3, TEST_UID2};
+ expectPrivilegedUserSet(uidRemain);
+
+ mTc.setPermissionForUids(INetd::PERMISSION_NONE, uidRemain);
+ expectPrivilegedUserSetEmpty();
+}
+
+TEST_F(TrafficControllerTest, TestGrantWrongPermission) {
+ std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3};
+
+ mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids);
+ expectPrivilegedUserSetEmpty();
+ expectUidPermissionMapValues(appUids, INetd::PERMISSION_NONE);
+}
+
+TEST_F(TrafficControllerTest, TestGrantDuplicatePermissionSlientlyFail) {
+ std::vector<uid_t> appUids = {TEST_UID, TEST_UID2, TEST_UID3};
+
+ mTc.setPermissionForUids(INetd::PERMISSION_INTERNET, appUids);
+ expectMapEmpty(mFakeUidPermissionMap);
+
+ std::vector<uid_t> uidToAdd = {TEST_UID};
+ mTc.setPermissionForUids(INetd::PERMISSION_INTERNET, uidToAdd);
+
+ expectPrivilegedUserSetEmpty();
+
+ mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids);
+ expectUidPermissionMapValues(appUids, INetd::PERMISSION_NONE);
+
+ mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, appUids);
+ expectPrivilegedUserSet(appUids);
+
+ mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, uidToAdd);
+ expectPrivilegedUserSet(appUids);
+
+ mTc.setPermissionForUids(INetd::PERMISSION_NONE, appUids);
+ expectPrivilegedUserSetEmpty();
+}
+
+} // namespace net
+} // namespace android
diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h
new file mode 100644
index 0000000..3e98b68
--- /dev/null
+++ b/service/native/include/TrafficController.h
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+#ifndef NETD_SERVER_TRAFFIC_CONTROLLER_H
+#define NETD_SERVER_TRAFFIC_CONTROLLER_H
+
+#include <linux/bpf.h>
+
+#include "NetlinkListener.h"
+#include "android-base/thread_annotations.h"
+#include "bpf/BpfMap.h"
+#include "bpf_shared.h"
+#include "netdutils/DumpWriter.h"
+#include "netdutils/NetlinkListener.h"
+#include "netdutils/StatusOr.h"
+#include "utils/String16.h"
+
+namespace android {
+namespace net {
+
+class TrafficController {
+ public:
+ /*
+ * Initialize the whole controller
+ */
+ netdutils::Status start();
+
+ /*
+ * Similiar as above, no external lock required.
+ */
+ int setCounterSet(int counterSetNum, uid_t uid, uid_t callingUid) EXCLUDES(mMutex);
+
+ /*
+ * When deleting a tag data, the qtaguid module will grab the spinlock of each
+ * related rb_tree one by one and delete the tag information, counterSet
+ * information, iface stats information and uid stats information one by one.
+ * The new eBPF implementation is done similiarly by removing the entry on
+ * each map one by one. And deleting processes are also protected by the
+ * spinlock of the map. So no additional lock is required.
+ */
+ int deleteTagData(uint32_t tag, uid_t uid, uid_t callingUid) EXCLUDES(mMutex);
+
+ /*
+ * Swap the stats map config from current active stats map to the idle one.
+ */
+ netdutils::Status swapActiveStatsMap() EXCLUDES(mMutex);
+
+ /*
+ * Add the interface name and index pair into the eBPF map.
+ */
+ int addInterface(const char* name, uint32_t ifaceIndex);
+
+ int changeUidOwnerRule(ChildChain chain, const uid_t uid, FirewallRule rule, FirewallType type);
+
+ int removeUidOwnerRule(const uid_t uid);
+
+ int replaceUidOwnerMap(const std::string& name, bool isAllowlist,
+ const std::vector<int32_t>& uids);
+
+ enum IptOp { IptOpInsert, IptOpDelete };
+
+ netdutils::Status updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid, FirewallRule rule,
+ FirewallType type) EXCLUDES(mMutex);
+
+ void dump(netdutils::DumpWriter& dw, bool verbose) EXCLUDES(mMutex);
+
+ netdutils::Status replaceRulesInMap(UidOwnerMatchType match, const std::vector<int32_t>& uids)
+ EXCLUDES(mMutex);
+
+ netdutils::Status addUidInterfaceRules(const int ifIndex, const std::vector<int32_t>& uids)
+ EXCLUDES(mMutex);
+ netdutils::Status removeUidInterfaceRules(const std::vector<int32_t>& uids) EXCLUDES(mMutex);
+
+ netdutils::Status updateUidOwnerMap(const std::vector<uint32_t>& appStrUids,
+ UidOwnerMatchType matchType, IptOp op) EXCLUDES(mMutex);
+ static const String16 DUMP_KEYWORD;
+
+ int toggleUidOwnerMap(ChildChain chain, bool enable) EXCLUDES(mMutex);
+
+ static netdutils::StatusOr<std::unique_ptr<netdutils::NetlinkListenerInterface>>
+ makeSkDestroyListener();
+
+ void setPermissionForUids(int permission, const std::vector<uid_t>& uids) EXCLUDES(mMutex);
+
+ FirewallType getFirewallType(ChildChain);
+
+ static const char* LOCAL_DOZABLE;
+ static const char* LOCAL_STANDBY;
+ static const char* LOCAL_POWERSAVE;
+ static const char* LOCAL_RESTRICTED;
+
+ private:
+ /*
+ * mCookieTagMap: Store the corresponding tag and uid for a specific socket.
+ * DO NOT hold any locks when modifying this map, otherwise when the untag
+ * operation is waiting for a lock hold by other process and there are more
+ * sockets being closed than can fit in the socket buffer of the netlink socket
+ * that receives them, then the kernel will drop some of these sockets and we
+ * won't delete their tags.
+ * Map Key: uint64_t socket cookie
+ * Map Value: UidTagValue, contains a uint32 uid and a uint32 tag.
+ */
+ bpf::BpfMap<uint64_t, UidTagValue> mCookieTagMap GUARDED_BY(mMutex);
+
+ /*
+ * mUidCounterSetMap: Store the counterSet of a specific uid.
+ * Map Key: uint32 uid.
+ * Map Value: uint32 counterSet specifies if the traffic is a background
+ * or foreground traffic.
+ */
+ bpf::BpfMap<uint32_t, uint8_t> mUidCounterSetMap GUARDED_BY(mMutex);
+
+ /*
+ * mAppUidStatsMap: Store the total traffic stats for a uid regardless of
+ * tag, counterSet and iface. The stats is used by TrafficStats.getUidStats
+ * API to return persistent stats for a specific uid since device boot.
+ */
+ bpf::BpfMap<uint32_t, StatsValue> mAppUidStatsMap;
+
+ /*
+ * mStatsMapA/mStatsMapB: Store the traffic statistics for a specific
+ * combination of uid, tag, iface and counterSet. These two maps contain
+ * both tagged and untagged traffic.
+ * Map Key: StatsKey contains the uid, tag, counterSet and ifaceIndex
+ * information.
+ * Map Value: Stats, contains packet count and byte count of each
+ * transport protocol on egress and ingress direction.
+ */
+ bpf::BpfMap<StatsKey, StatsValue> mStatsMapA GUARDED_BY(mMutex);
+
+ bpf::BpfMap<StatsKey, StatsValue> mStatsMapB GUARDED_BY(mMutex);
+
+ /*
+ * mIfaceIndexNameMap: Store the index name pair of each interface show up
+ * on the device since boot. The interface index is used by the eBPF program
+ * to correctly match the iface name when receiving a packet.
+ */
+ bpf::BpfMap<uint32_t, IfaceValue> mIfaceIndexNameMap;
+
+ /*
+ * mIfaceStataMap: Store per iface traffic stats gathered from xt_bpf
+ * filter.
+ */
+ bpf::BpfMap<uint32_t, StatsValue> mIfaceStatsMap;
+
+ /*
+ * mConfigurationMap: Store the current network policy about uid filtering
+ * and the current stats map in use. There are two configuration entries in
+ * the map right now:
+ * - Entry with UID_RULES_CONFIGURATION_KEY:
+ * Store the configuration for the current uid rules. It indicates the device
+ * is in doze/powersave/standby/restricted mode.
+ * - Entry with CURRENT_STATS_MAP_CONFIGURATION_KEY:
+ * Stores the current live stats map that kernel program is writing to.
+ * Userspace can do scraping and cleaning job on the other one depending on the
+ * current configs.
+ */
+ bpf::BpfMap<uint32_t, uint8_t> mConfigurationMap GUARDED_BY(mMutex);
+
+ /*
+ * mUidOwnerMap: Store uids that are used for bandwidth control uid match.
+ */
+ bpf::BpfMap<uint32_t, UidOwnerValue> mUidOwnerMap GUARDED_BY(mMutex);
+
+ /*
+ * mUidOwnerMap: Store uids that are used for INTERNET permission check.
+ */
+ bpf::BpfMap<uint32_t, uint8_t> mUidPermissionMap GUARDED_BY(mMutex);
+
+ std::unique_ptr<netdutils::NetlinkListenerInterface> mSkDestroyListener;
+
+ netdutils::Status removeRule(uint32_t uid, UidOwnerMatchType match) REQUIRES(mMutex);
+
+ netdutils::Status addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif = 0)
+ REQUIRES(mMutex);
+
+ // mMutex guards all accesses to mConfigurationMap, mUidOwnerMap, mUidPermissionMap,
+ // mStatsMapA, mStatsMapB and mPrivilegedUser. It is designed to solve the following
+ // problems:
+ // 1. Prevent concurrent access and modification to mConfigurationMap, mUidOwnerMap,
+ // mUidPermissionMap, and mPrivilegedUser. These data members are controlled by netd but can
+ // be modified from different threads. TrafficController provides several APIs directly
+ // called by the binder RPC, and different binder threads can concurrently access these data
+ // members mentioned above. Some of the data members such as mUidPermissionMap and
+ // mPrivilegedUsers are also accessed from a different thread when tagging sockets or
+ // setting the counterSet through FwmarkServer
+ // 2. Coordinate the deletion of uid stats in mStatsMapA and mStatsMapB. The system server
+ // always call into netd to ask for a live stats map change before it pull and clean up the
+ // stats from the inactive map. The mMutex will block netd from accessing the stats map when
+ // the mConfigurationMap is updating the current stats map so netd will not accidentally
+ // read the map that system_server is cleaning up.
+ std::mutex mMutex;
+
+ netdutils::Status initMaps() EXCLUDES(mMutex);
+
+ // Keep track of uids that have permission UPDATE_DEVICE_STATS so we don't
+ // need to call back to system server for permission check.
+ std::set<uid_t> mPrivilegedUser GUARDED_BY(mMutex);
+
+ bool hasUpdateDeviceStatsPermission(uid_t uid) REQUIRES(mMutex);
+
+ // For testing
+ friend class TrafficControllerTest;
+};
+
+} // namespace net
+} // namespace android
+
+#endif // NETD_SERVER_TRAFFIC_CONTROLLER_H
diff --git a/service/native/libs/libclat/Android.bp b/service/native/libs/libclat/Android.bp
new file mode 100644
index 0000000..8540787
--- /dev/null
+++ b/service/native/libs/libclat/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_static {
+ name: "libclat",
+ defaults: ["netd_defaults"],
+ srcs: ["clatutils.cpp"],
+ stl: "libc++_static",
+ static_libs: ["libip_checksum"],
+ shared_libs: ["liblog"],
+ export_include_dirs: ["include"],
+ min_sdk_version: "30",
+ apex_available: ["com.android.tethering"],
+}
+
+cc_test {
+ name: "libclat_test",
+ defaults: ["netd_defaults"],
+ test_suites: ["device-tests"],
+ srcs: [
+ "clatutils_test.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libclat",
+ "libip_checksum",
+ "libnetd_test_tun_interface",
+ ],
+ shared_libs: [
+ "liblog",
+ "libnetutils",
+ ],
+ require_root: true,
+}
\ No newline at end of file
diff --git a/service/native/libs/libclat/clatutils.cpp b/service/native/libs/libclat/clatutils.cpp
new file mode 100644
index 0000000..4a125ba
--- /dev/null
+++ b/service/native/libs/libclat/clatutils.cpp
@@ -0,0 +1,268 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#define LOG_TAG "clatutils"
+
+#include "libclat/clatutils.h"
+
+#include <errno.h>
+#include <linux/filter.h>
+#include <linux/if_packet.h>
+#include <linux/if_tun.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern "C" {
+#include "checksum.h"
+}
+
+// Sync from external/android-clat/clatd.h
+#define MAXMTU 65536
+#define PACKETLEN (MAXMTU + sizeof(struct tun_pi))
+
+// Sync from system/netd/include/netid_client.h.
+#define MARK_UNSET 0u
+
+namespace android {
+namespace net {
+namespace clat {
+
+bool isIpv4AddressFree(in_addr_t addr) {
+ int s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (s == -1) {
+ return 0;
+ }
+
+ // Attempt to connect to the address. If the connection succeeds and getsockname returns the
+ // same then the address is already assigned to the system and we can't use it.
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_port = htons(53),
+ .sin_addr = {addr},
+ };
+ socklen_t len = sizeof(sin);
+ bool inuse = connect(s, (struct sockaddr*)&sin, sizeof(sin)) == 0 &&
+ getsockname(s, (struct sockaddr*)&sin, &len) == 0 && (size_t)len >= sizeof(sin) &&
+ sin.sin_addr.s_addr == addr;
+
+ close(s);
+ return !inuse;
+}
+
+// Picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order.
+// ip - the IP address from the configuration file
+// prefixlen - the length of the prefix from which addresses may be selected.
+// returns: the IPv4 address, or INADDR_NONE if no addresses were available
+in_addr_t selectIpv4Address(const in_addr ip, int16_t prefixlen) {
+ return selectIpv4AddressInternal(ip, prefixlen, isIpv4AddressFree);
+}
+
+// Only allow testing to use this function directly. Otherwise call selectIpv4Address(ip, pfxlen)
+// which has applied valid isIpv4AddressFree function pointer.
+in_addr_t selectIpv4AddressInternal(const in_addr ip, int16_t prefixlen,
+ isIpv4AddrFreeFn isIpv4AddressFreeFunc) {
+ // Impossible! Only test allows to apply fn.
+ if (isIpv4AddressFreeFunc == nullptr) {
+ return INADDR_NONE;
+ }
+
+ // Don't accept prefixes that are too large because we scan addresses one by one.
+ if (prefixlen < 16 || prefixlen > 32) {
+ return INADDR_NONE;
+ }
+
+ // All these are in host byte order.
+ in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen);
+ in_addr_t ipv4 = ntohl(ip.s_addr);
+ in_addr_t first_ipv4 = ipv4;
+ in_addr_t prefix = ipv4 & mask;
+
+ // Pick the first IPv4 address in the pool, wrapping around if necessary.
+ // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0.
+ do {
+ if (isIpv4AddressFreeFunc(htonl(ipv4))) {
+ return htonl(ipv4);
+ }
+ ipv4 = prefix | ((ipv4 + 1) & ~mask);
+ } while (ipv4 != first_ipv4);
+
+ return INADDR_NONE;
+}
+
+// Alters the bits in the IPv6 address to make them checksum neutral with v4 and nat64Prefix.
+void makeChecksumNeutral(in6_addr* v6, const in_addr v4, const in6_addr& nat64Prefix) {
+ // Fill last 8 bytes of IPv6 address with random bits.
+ arc4random_buf(&v6->s6_addr[8], 8);
+
+ // Make the IID checksum-neutral. That is, make it so that:
+ // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6)
+ // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4):
+ // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix)
+ // Do this by adjusting the two bytes in the middle of the IID.
+
+ uint16_t middlebytes = (v6->s6_addr[11] << 8) + v6->s6_addr[12];
+
+ uint32_t c1 = ip_checksum_add(0, &v4, sizeof(v4));
+ uint32_t c2 = ip_checksum_add(0, &nat64Prefix, sizeof(nat64Prefix)) +
+ ip_checksum_add(0, v6, sizeof(*v6));
+
+ uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2);
+ v6->s6_addr[11] = delta >> 8;
+ v6->s6_addr[12] = delta & 0xff;
+}
+
+// Picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix.
+int generateIpv6Address(const char* iface, const in_addr v4, const in6_addr& nat64Prefix,
+ in6_addr* v6) {
+ int s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (s == -1) return -errno;
+
+ if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface) + 1) == -1) {
+ close(s);
+ return -errno;
+ }
+
+ sockaddr_in6 sin6 = {.sin6_family = AF_INET6, .sin6_addr = nat64Prefix};
+ if (connect(s, reinterpret_cast<struct sockaddr*>(&sin6), sizeof(sin6)) == -1) {
+ close(s);
+ return -errno;
+ }
+
+ socklen_t len = sizeof(sin6);
+ if (getsockname(s, reinterpret_cast<struct sockaddr*>(&sin6), &len) == -1) {
+ close(s);
+ return -errno;
+ }
+
+ *v6 = sin6.sin6_addr;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(v6) || IN6_IS_ADDR_LOOPBACK(v6) || IN6_IS_ADDR_LINKLOCAL(v6) ||
+ IN6_IS_ADDR_SITELOCAL(v6) || IN6_IS_ADDR_ULA(v6)) {
+ close(s);
+ return -ENETUNREACH;
+ }
+
+ makeChecksumNeutral(v6, v4, nat64Prefix);
+ close(s);
+
+ return 0;
+}
+
+int detect_mtu(const struct in6_addr* plat_subnet, uint32_t plat_suffix, uint32_t mark) {
+ // Create an IPv6 UDP socket.
+ int s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (s < 0) {
+ int ret = errno;
+ ALOGE("socket(AF_INET6, SOCK_DGRAM, 0) failed: %s", strerror(errno));
+ return -ret;
+ }
+
+ // Socket's mark affects routing decisions (network selection)
+ if ((mark != MARK_UNSET) && setsockopt(s, SOL_SOCKET, SO_MARK, &mark, sizeof(mark))) {
+ int ret = errno;
+ ALOGE("setsockopt(SOL_SOCKET, SO_MARK) failed: %s", strerror(errno));
+ close(s);
+ return -ret;
+ }
+
+ // Try to connect udp socket to plat_subnet(96 bits):plat_suffix(32 bits)
+ struct sockaddr_in6 dst = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = *plat_subnet,
+ };
+ dst.sin6_addr.s6_addr32[3] = plat_suffix;
+ if (connect(s, (struct sockaddr*)&dst, sizeof(dst))) {
+ int ret = errno;
+ ALOGE("connect() failed: %s", strerror(errno));
+ close(s);
+ return -ret;
+ }
+
+ // Fetch the socket's IPv6 mtu - this is effectively fetching mtu from routing table
+ int mtu;
+ socklen_t sz_mtu = sizeof(mtu);
+ if (getsockopt(s, SOL_IPV6, IPV6_MTU, &mtu, &sz_mtu)) {
+ int ret = errno;
+ ALOGE("getsockopt(SOL_IPV6, IPV6_MTU) failed: %s", strerror(errno));
+ close(s);
+ return -ret;
+ }
+ if (sz_mtu != sizeof(mtu)) {
+ ALOGE("getsockopt(SOL_IPV6, IPV6_MTU) returned unexpected size: %d", sz_mtu);
+ close(s);
+ return -EFAULT;
+ }
+ close(s);
+
+ return mtu;
+}
+
+/* function: configure_packet_socket
+ * Binds the packet socket and attaches the receive filter to it.
+ * sock - the socket to configure
+ * addr - the IP address to filter
+ * ifindex - index of interface to add the filter to
+ * returns: 0 on success, -errno on failure
+ */
+int configure_packet_socket(int sock, in6_addr* addr, int ifindex) {
+ uint32_t* ipv6 = addr->s6_addr32;
+
+ // clang-format off
+ struct sock_filter filter_code[] = {
+ // Load the first four bytes of the IPv6 destination address (starts 24 bytes in).
+ // Compare it against the first four bytes of our IPv6 address, in host byte order (BPF loads
+ // are always in host byte order). If it matches, continue with next instruction (JMP 0). If it
+ // doesn't match, jump ahead to statement that returns 0 (ignore packet). Repeat for the other
+ // three words of the IPv6 address, and if they all match, return PACKETLEN (accept packet).
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 24),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[0]), 0, 7),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 28),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[1]), 0, 5),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 32),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[2]), 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 36),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[3]), 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, PACKETLEN),
+ BPF_STMT(BPF_RET | BPF_K, 0),
+ };
+ // clang-format on
+ struct sock_fprog filter = {sizeof(filter_code) / sizeof(filter_code[0]), filter_code};
+
+ if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
+ int res = errno;
+ ALOGE("attach packet filter failed: %s", strerror(errno));
+ return -res;
+ }
+
+ struct sockaddr_ll sll = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_IPV6),
+ .sll_ifindex = ifindex,
+ .sll_pkttype =
+ PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel.
+ };
+ if (bind(sock, (struct sockaddr*)&sll, sizeof(sll))) {
+ int res = errno;
+ ALOGE("binding packet socket: %s", strerror(errno));
+ return -res;
+ }
+
+ return 0;
+}
+
+} // namespace clat
+} // namespace net
+} // namespace android
diff --git a/service/native/libs/libclat/clatutils_test.cpp b/service/native/libs/libclat/clatutils_test.cpp
new file mode 100644
index 0000000..4153e19
--- /dev/null
+++ b/service/native/libs/libclat/clatutils_test.cpp
@@ -0,0 +1,187 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "libclat/clatutils.h"
+
+#include <android-base/stringprintf.h>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <linux/if_packet.h>
+#include <linux/if_tun.h>
+#include "tun_interface.h"
+
+extern "C" {
+#include "checksum.h"
+}
+
+// Default translation parameters.
+static const char kIPv4LocalAddr[] = "192.0.0.4";
+
+namespace android {
+namespace net {
+namespace clat {
+
+using android::net::TunInterface;
+using base::StringPrintf;
+
+class ClatUtils : public ::testing::Test {};
+
+// Mock functions for isIpv4AddressFree.
+bool neverFree(in_addr_t /* addr */) {
+ return 0;
+}
+bool alwaysFree(in_addr_t /* addr */) {
+ return 1;
+}
+bool only2Free(in_addr_t addr) {
+ return (ntohl(addr) & 0xff) == 2;
+}
+bool over6Free(in_addr_t addr) {
+ return (ntohl(addr) & 0xff) >= 6;
+}
+bool only10Free(in_addr_t addr) {
+ return (ntohl(addr) & 0xff) == 10;
+}
+
+// Apply mocked isIpv4AddressFree function for selectIpv4Address test.
+in_addr_t selectIpv4Address(const in_addr ip, int16_t prefixlen,
+ isIpv4AddrFreeFn fn /* mocked function */) {
+ // Call internal function to replace isIpv4AddressFreeFn for testing.
+ return selectIpv4AddressInternal(ip, prefixlen, fn);
+}
+
+TEST_F(ClatUtils, SelectIpv4Address) {
+ struct in_addr addr;
+
+ inet_pton(AF_INET, kIPv4LocalAddr, &addr);
+
+ // If no addresses are free, return INADDR_NONE.
+ EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 29, neverFree));
+ EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 16, neverFree));
+
+ // If the configured address is free, pick that. But a prefix that's too big is invalid.
+ EXPECT_EQ(inet_addr(kIPv4LocalAddr), selectIpv4Address(addr, 29, alwaysFree));
+ EXPECT_EQ(inet_addr(kIPv4LocalAddr), selectIpv4Address(addr, 20, alwaysFree));
+ EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 15, alwaysFree));
+
+ // A prefix length of 32 works, but anything above it is invalid.
+ EXPECT_EQ(inet_addr(kIPv4LocalAddr), selectIpv4Address(addr, 32, alwaysFree));
+ EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 33, alwaysFree));
+
+ // If another address is free, pick it.
+ EXPECT_EQ(inet_addr("192.0.0.6"), selectIpv4Address(addr, 29, over6Free));
+
+ // Check that we wrap around to addresses that are lower than the first address.
+ EXPECT_EQ(inet_addr("192.0.0.2"), selectIpv4Address(addr, 29, only2Free));
+ EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 30, only2Free));
+
+ // If a free address exists outside the prefix, we don't pick it.
+ EXPECT_EQ(INADDR_NONE, selectIpv4Address(addr, 29, only10Free));
+ EXPECT_EQ(inet_addr("192.0.0.10"), selectIpv4Address(addr, 24, only10Free));
+
+ // Now try using the real function which sees if IP addresses are free using bind().
+ // Assume that the machine running the test has the address 127.0.0.1, but not 8.8.8.8.
+ addr.s_addr = inet_addr("8.8.8.8");
+ EXPECT_EQ(inet_addr("8.8.8.8"), selectIpv4Address(addr, 29));
+
+ addr.s_addr = inet_addr("127.0.0.1");
+ EXPECT_EQ(inet_addr("127.0.0.2"), selectIpv4Address(addr, 29));
+}
+
+TEST_F(ClatUtils, MakeChecksumNeutral) {
+ // We can't test generateIPv6Address here since it requires manipulating routing, which we can't
+ // do without talking to the real netd on the system.
+ uint32_t rand = arc4random_uniform(0xffffffff);
+ uint16_t rand1 = rand & 0xffff;
+ uint16_t rand2 = (rand >> 16) & 0xffff;
+ std::string v6PrefixStr = StringPrintf("2001:db8:%x:%x", rand1, rand2);
+ std::string v6InterfaceAddrStr = StringPrintf("%s::%x:%x", v6PrefixStr.c_str(), rand2, rand1);
+ std::string nat64PrefixStr = StringPrintf("2001:db8:%x:%x::", rand2, rand1);
+
+ in_addr v4 = {inet_addr(kIPv4LocalAddr)};
+ in6_addr v6InterfaceAddr;
+ ASSERT_TRUE(inet_pton(AF_INET6, v6InterfaceAddrStr.c_str(), &v6InterfaceAddr));
+ in6_addr nat64Prefix;
+ ASSERT_TRUE(inet_pton(AF_INET6, nat64PrefixStr.c_str(), &nat64Prefix));
+
+ // Generate a boatload of random IIDs.
+ int onebits = 0;
+ uint64_t prev_iid = 0;
+ for (int i = 0; i < 100000; i++) {
+ in6_addr v6 = v6InterfaceAddr;
+ makeChecksumNeutral(&v6, v4, nat64Prefix);
+
+ // Check the generated IP address is in the same prefix as the interface IPv6 address.
+ EXPECT_EQ(0, memcmp(&v6, &v6InterfaceAddr, 8));
+
+ // Check that consecutive IIDs are not the same.
+ uint64_t iid = *(uint64_t*)(&v6.s6_addr[8]);
+ ASSERT_TRUE(iid != prev_iid)
+ << "Two consecutive random IIDs are the same: " << std::showbase << std::hex << iid
+ << "\n";
+ prev_iid = iid;
+
+ // Check that the IID is checksum-neutral with the NAT64 prefix and the
+ // local prefix.
+ uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, &v4, sizeof(v4)));
+ uint16_t c2 = ip_checksum_finish(ip_checksum_add(0, &nat64Prefix, sizeof(nat64Prefix)) +
+ ip_checksum_add(0, &v6, sizeof(v6)));
+
+ if (c1 != c2) {
+ char v6Str[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &v6, v6Str, sizeof(v6Str));
+ FAIL() << "Bad IID: " << v6Str << " not checksum-neutral with " << kIPv4LocalAddr
+ << " and " << nat64PrefixStr.c_str() << std::showbase << std::hex
+ << "\n IPv4 checksum: " << c1 << "\n IPv6 checksum: " << c2 << "\n";
+ }
+
+ // Check that IIDs are roughly random and use all the bits by counting the
+ // total number of bits set to 1 in a random sample of 100000 generated IIDs.
+ onebits += __builtin_popcountll(*(uint64_t*)&iid);
+ }
+ EXPECT_LE(3190000, onebits);
+ EXPECT_GE(3210000, onebits);
+}
+
+TEST_F(ClatUtils, DetectMtu) {
+ // ::1 with bottom 32 bits set to 1 is still ::1 which routes via lo with mtu of 64KiB
+ ASSERT_EQ(detect_mtu(&in6addr_loopback, htonl(1), 0 /*MARK_UNSET*/), 65536);
+}
+
+TEST_F(ClatUtils, ConfigurePacketSocket) {
+ // Create an interface for configure_packet_socket to attach socket filter to.
+ TunInterface v6Iface;
+ ASSERT_EQ(0, v6Iface.init());
+
+ int s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
+ EXPECT_LE(0, s);
+ struct in6_addr addr6;
+ EXPECT_EQ(1, inet_pton(AF_INET6, "2001:db8::f00", &addr6));
+ EXPECT_EQ(0, configure_packet_socket(s, &addr6, v6Iface.ifindex()));
+
+ // Check that the packet socket is bound to the interface. We can't check the socket filter
+ // because there is no way to fetch it from the kernel.
+ sockaddr_ll sll;
+ socklen_t len = sizeof(sll);
+ ASSERT_EQ(0, getsockname(s, reinterpret_cast<sockaddr*>(&sll), &len));
+ EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol);
+ EXPECT_EQ(sll.sll_ifindex, v6Iface.ifindex());
+
+ close(s);
+ v6Iface.destroy();
+}
+
+} // namespace clat
+} // namespace net
+} // namespace android
diff --git a/service/native/libs/libclat/include/libclat/clatutils.h b/service/native/libs/libclat/include/libclat/clatutils.h
new file mode 100644
index 0000000..812c86e
--- /dev/null
+++ b/service/native/libs/libclat/include/libclat/clatutils.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include <netinet/in.h>
+#include <netinet/in6.h>
+
+namespace android {
+namespace net {
+namespace clat {
+
+bool isIpv4AddressFree(in_addr_t addr);
+in_addr_t selectIpv4Address(const in_addr ip, int16_t prefixlen);
+void makeChecksumNeutral(in6_addr* v6, const in_addr v4, const in6_addr& nat64Prefix);
+int generateIpv6Address(const char* iface, const in_addr v4, const in6_addr& nat64Prefix,
+ in6_addr* v6);
+int detect_mtu(const struct in6_addr* plat_subnet, uint32_t plat_suffix, uint32_t mark);
+int configure_packet_socket(int sock, in6_addr* addr, int ifindex);
+
+// For testing
+typedef bool (*isIpv4AddrFreeFn)(in_addr_t);
+in_addr_t selectIpv4AddressInternal(const in_addr ip, int16_t prefixlen, isIpv4AddrFreeFn fn);
+
+} // namespace clat
+} // namespace net
+} // namespace android
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
new file mode 100644
index 0000000..bc63eef
--- /dev/null
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.os.ServiceSpecificException;
+import android.system.Os;
+import android.util.Log;
+
+/**
+ * BpfNetMaps is responsible for providing traffic controller relevant functionality.
+ *
+ * {@hide}
+ */
+public class BpfNetMaps {
+ private static final String TAG = "BpfNetMaps";
+
+ /**
+ * Add naughty app bandwidth rule for specific app
+ *
+ * @param uid uid of target app
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void addNaughtyApp(final int uid) {
+ final int err = native_addNaughtyApp(uid);
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to add naughty app: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Remove naughty app bandwidth rule for specific app
+ *
+ * @param uid uid of target app
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void removeNaughtyApp(final int uid) {
+ final int err = native_removeNaughtyApp(uid);
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to remove naughty app: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Add nice app bandwidth rule for specific app
+ *
+ * @param uid uid of target app
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void addNiceApp(final int uid) {
+ final int err = native_addNiceApp(uid);
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to add nice app: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Remove nice app bandwidth rule for specific app
+ *
+ * @param uid uid of target app
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void removeNiceApp(final int uid) {
+ final int err = native_removeNiceApp(uid);
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to remove nice app: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Set target firewall child chain
+ *
+ * @param childChain target chain to enable
+ * @param enable whether to enable or disable child chain.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void setChildChain(final int childChain, final boolean enable) {
+ final int err = native_setChildChain(childChain, enable);
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to set child chain: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Replaces the contents of the specified UID-based firewall chain.
+ *
+ * The chain may be an allowlist chain or a denylist chain. A denylist chain contains DROP
+ * rules for the specified UIDs and a RETURN rule at the end. An allowlist chain contains RETURN
+ * rules for the system UID range (0 to {@code UID_APP} - 1), RETURN rules for for the specified
+ * UIDs, and a DROP rule at the end. The chain will be created if it does not exist.
+ *
+ * @param chainName The name of the chain to replace.
+ * @param isAllowlist Whether this is an allowlist or denylist chain.
+ * @param uids The list of UIDs to allow/deny.
+ * @return true if the chain was successfully replaced, false otherwise.
+ */
+ public int replaceUidChain(final String chainName, final boolean isAllowlist,
+ final int[] uids) {
+ final int err = native_replaceUidChain(chainName, isAllowlist, uids);
+ if (err != 0) {
+ Log.e(TAG, "replaceUidChain failed: " + Os.strerror(-err));
+ }
+ return -err;
+ }
+
+ /**
+ * Set firewall rule for uid
+ *
+ * @param childChain target chain
+ * @param uid uid to allow/deny
+ * @param firewallRule either FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void setUidRule(final int childChain, final int uid,
+ final int firewallRule) {
+ final int err = native_setUidRule(childChain, uid, firewallRule);
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to set uid rule: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Add ingress interface filtering rules to a list of UIDs
+ *
+ * For a given uid, once a filtering rule is added, the kernel will only allow packets from the
+ * allowed interface and loopback to be sent to the list of UIDs.
+ *
+ * Calling this method on one or more UIDs with an existing filtering rule but a different
+ * interface name will result in the filtering rule being updated to allow the new interface
+ * instead. Otherwise calling this method will not affect existing rules set on other UIDs.
+ *
+ * @param ifName the name of the interface on which the filtering rules will allow packets to
+ be received.
+ * @param uids an array of UIDs which the filtering rules will be set
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void addUidInterfaceRules(final String ifName, final int[] uids) {
+ final int err = native_addUidInterfaceRules(ifName, uids);
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to add uid interface rules: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Remove ingress interface filtering rules from a list of UIDs
+ *
+ * Clear the ingress interface filtering rules from the list of UIDs which were previously set
+ * by addUidInterfaceRules(). Ignore any uid which does not have filtering rule.
+ *
+ * @param uids an array of UIDs from which the filtering rules will be removed
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void removeUidInterfaceRules(final int[] uids) {
+ final int err = native_removeUidInterfaceRules(uids);
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to remove uid interface rules: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Request netd to change the current active network stats map.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ public void swapActiveStatsMap() {
+ final int err = native_swapActiveStatsMap();
+ if (err != 0) {
+ throw new ServiceSpecificException(-err, "Unable to swap active stats map: "
+ + Os.strerror(-err));
+ }
+ }
+
+ /**
+ * Assigns android.permission.INTERNET and/or android.permission.UPDATE_DEVICE_STATS to the uids
+ * specified. Or remove all permissions from the uids.
+ *
+ * @param permission The permission to grant, it could be either PERMISSION_INTERNET and/or
+ * PERMISSION_UPDATE_DEVICE_STATS. If the permission is NO_PERMISSIONS, then
+ * revoke all permissions for the uids.
+ * @param uids uid of users to grant permission
+ */
+ public void setNetPermForUids(final int permission, final int[] uids) {
+ native_setPermissionForUids(permission, uids);
+ }
+
+ /**
+ * Set counter set for uid
+ *
+ * @param counterSet either SET_DEFAULT or SET_FOREGROUND
+ * @param uid uid to foreground/background
+ */
+ public int setCounterSet(final int counterSet, final int uid) {
+ final int err = native_setCounterSet(counterSet, uid);
+ if (err != 0) {
+ Log.e(TAG, "setCounterSet failed: " + Os.strerror(-err));
+ }
+ return -err;
+ }
+
+ /**
+ * Reset Uid stats
+ * @param tag default 0
+ * @param uid given uid to be clear
+ */
+ public int deleteTagData(final int tag, final int uid) {
+ final int err = native_deleteTagData(tag, uid);
+ if (err != 0) {
+ Log.e(TAG, "deleteTagData failed: " + Os.strerror(-err));
+ }
+ return -err;
+ }
+
+ private native int native_addNaughtyApp(int uid);
+ private native int native_removeNaughtyApp(int uid);
+ private native int native_addNiceApp(int uid);
+ private native int native_removeNiceApp(int uid);
+ private native int native_setChildChain(int childChain, boolean enable);
+ private native int native_replaceUidChain(String name, boolean isAllowlist, int[] uids);
+ private native int native_setUidRule(int childChain, int uid, int firewallRule);
+ private native int native_addUidInterfaceRules(String ifName, int[] uids);
+ private native int native_removeUidInterfaceRules(int[] uids);
+ private native int native_swapActiveStatsMap();
+ private native void native_setPermissionForUids(int permission, int[] uids);
+ private native int native_setCounterSet(int counterSet, int uid);
+ private native int native_deleteTagData(int tag, int uid);
+}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 1d5d8af..6c27c4a 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -15,6 +15,7 @@
*/
package com.android.server;
+
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -80,19 +81,24 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
+import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID;
import static android.os.Process.VPN_UID;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
+import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
+
import static java.util.Map.Entry;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
@@ -160,6 +166,7 @@
import android.net.NetworkWatchlistManager;
import android.net.OemNetworkPreferences;
import android.net.PrivateDnsConfigParcel;
+import android.net.ProfileNetworkPreference;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
import android.net.QosFilter;
@@ -227,8 +234,10 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.LocationPermissionChecker;
@@ -236,6 +245,7 @@
import com.android.net.module.util.PermissionUtils;
import com.android.net.module.util.netlink.InetDiagMessage;
import com.android.server.connectivity.AutodestructReference;
+import com.android.server.connectivity.ConnectivityFlags;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.FullScore;
@@ -249,14 +259,16 @@
import com.android.server.connectivity.NetworkOffer;
import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
-import com.android.server.connectivity.ProfileNetworkPreferences;
+import com.android.server.connectivity.ProfileNetworkPreferenceList;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
+import com.android.server.connectivity.UidRangeUtils;
import libcore.io.IoUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.io.Writer;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -271,6 +283,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
@@ -335,6 +348,9 @@
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
@VisibleForTesting
protected int mNascentDelayMs;
+ // True if the cell radio of the device is capable of time-sharing.
+ @VisibleForTesting
+ protected boolean mCellularRadioTimesharingCapable = true;
// How long to delay to removal of a pending intent based request.
// See ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
@@ -363,6 +379,7 @@
// The Context is created for UserHandle.ALL.
private final Context mUserAllContext;
private final Dependencies mDeps;
+ private final ConnectivityFlags mFlags;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -839,6 +856,9 @@
mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
}
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is
+ // addressed.
+ @TargetApi(Build.VERSION_CODES.S)
public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) {
final PackageManager pm = ctx.getPackageManager();
if (pm.hasSystemFeature(FEATURE_WIFI)) {
@@ -1196,35 +1216,6 @@
mUidToNetworkRequestCount.put(uid, newRequestCount);
}
}
-
- /**
- * Used to adjust the request counter for the per-app API flows. Directly adjusting the
- * counter is not ideal however in the per-app flows, the nris can't be removed until they
- * are used to create the new nris upon set. Therefore the request count limit can be
- * artificially hit. This method is used as a workaround for this particular case so that
- * the request counts are accounted for correctly.
- * @param uid the uid to adjust counts for
- * @param numOfNewRequests the new request count to account for
- * @param r the runnable to execute
- */
- public void transact(final int uid, final int numOfNewRequests, @NonNull final Runnable r) {
- // This should only be used on the handler thread as per all current and foreseen
- // use-cases. ensureRunningOnConnectivityServiceThread() can't be used because there is
- // no ref to the outer ConnectivityService.
- synchronized (mUidToNetworkRequestCount) {
- final int reqCountOverage = getCallingUidRequestCountOverage(uid, numOfNewRequests);
- decrementCount(uid, reqCountOverage);
- r.run();
- incrementCountOrThrow(uid, reqCountOverage);
- }
- }
-
- private int getCallingUidRequestCountOverage(final int uid, final int numOfNewRequests) {
- final int newUidRequestCount = mUidToNetworkRequestCount.get(uid, 0)
- + numOfNewRequests;
- return newUidRequestCount >= MAX_NETWORK_REQUESTS_PER_SYSTEM_UID
- ? newUidRequestCount - (MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - 1) : 0;
- }
}
/**
@@ -1330,6 +1321,14 @@
public LocationPermissionChecker makeLocationPermissionChecker(Context context) {
return new LocationPermissionChecker(context);
}
+
+ /**
+ * @see DeviceConfigUtils#isFeatureEnabled
+ */
+ public boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled) {
+ return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name,
+ TETHERING_MODULE_NAME, defaultEnabled);
+ }
}
public ConnectivityService(Context context) {
@@ -1344,6 +1343,7 @@
if (DBG) log("ConnectivityService starting up");
mDeps = Objects.requireNonNull(deps, "missing Dependencies");
+ mFlags = new ConnectivityFlags();
mSystemProperties = mDeps.getSystemProperties();
mNetIdManager = mDeps.makeNetIdManager();
mContext = Objects.requireNonNull(context, "missing Context");
@@ -1356,7 +1356,7 @@
final NetworkRequest defaultInternetRequest = createDefaultRequest();
mDefaultRequest = new NetworkRequestInfo(
Process.myUid(), defaultInternetRequest, null,
- new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
+ null /* binder */, NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
null /* attributionTags */);
mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
mDefaultNetworkRequests.add(mDefaultRequest);
@@ -1375,6 +1375,12 @@
NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
NetworkRequest.Type.BACKGROUND_REQUEST);
+ mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+ // TODO: Consider making the timer customizable.
+ mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
+ mCellularRadioTimesharingCapable =
+ mResources.get().getBoolean(R.bool.config_cellular_radio_timesharing_capable);
+
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -1385,10 +1391,6 @@
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
- mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
- // TODO: Consider making the timer customizable.
- mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
-
mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
mDnsResolver = Objects.requireNonNull(dnsresolver, "missing IDnsResolver");
@@ -1488,16 +1490,17 @@
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
- return createDefaultNetworkCapabilitiesForUidRange(new UidRange(uid, uid));
+ return createDefaultNetworkCapabilitiesForUidRangeSet(Collections.singleton(
+ new UidRange(uid, uid)));
}
- private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRange(
- @NonNull final UidRange uids) {
+ private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRangeSet(
+ @NonNull final Set<UidRange> uidRangeSet) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
- netCap.setUids(UidRange.toIntRanges(Collections.singleton(uids)));
+ netCap.setUids(UidRange.toIntRanges(uidRangeSet));
return netCap;
}
@@ -1574,7 +1577,7 @@
if (enable) {
handleRegisterNetworkRequest(new NetworkRequestInfo(
- Process.myUid(), networkRequest, null, new Binder(),
+ Process.myUid(), networkRequest, null /* messenger */, null /* binder */,
NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
null /* attributionTags */));
} else {
@@ -1590,12 +1593,7 @@
ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED, false /* defaultValue */);
final boolean vehicleAlwaysRequested = mResources.get().getBoolean(
R.bool.config_vehicleInternalNetworkAlwaysRequested);
- // TODO (b/183076074): remove legacy fallback after migrating overlays
- final boolean legacyAlwaysRequested = mContext.getResources().getBoolean(
- mContext.getResources().getIdentifier(
- "config_vehicleInternalNetworkAlwaysRequested", "bool", "android"));
- handleAlwaysOnNetworkRequest(mDefaultVehicleRequest,
- vehicleAlwaysRequested || legacyAlwaysRequested);
+ handleAlwaysOnNetworkRequest(mDefaultVehicleRequest, vehicleAlwaysRequested);
}
// Note that registering observer for setting do not get initial callback when registering,
@@ -2059,6 +2057,7 @@
if (!checkSettingsPermission(callerPid, callerUid)) {
newNc.setUids(null);
newNc.setSSID(null);
+ newNc.setUnderlyingNetworks(null);
}
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
@@ -2252,10 +2251,22 @@
private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc,
int callerUid, String callerPackageName) {
+ // There is no need to track the effective UID of the request here. If the caller
+ // lacks the settings permission, the effective UID is the same as the calling ID.
if (!checkSettingsPermission()) {
- // There is no need to track the effective UID of the request here. If the caller lacks
- // the settings permission, the effective UID is the same as the calling ID.
- nc.setSingleUid(callerUid);
+ // Unprivileged apps can only pass in null or their own UID.
+ if (nc.getUids() == null) {
+ // If the caller passes in null, the callback will also match networks that do not
+ // apply to its UID, similarly to what it would see if it called getAllNetworks.
+ // In this case, redact everything in the request immediately. This ensures that the
+ // app is not able to get any redacted information by filing an unredacted request
+ // and observing whether the request matches something.
+ if (nc.getNetworkSpecifier() != null) {
+ nc.setNetworkSpecifier(nc.getNetworkSpecifier().redact());
+ }
+ } else {
+ nc.setSingleUid(callerUid);
+ }
}
nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
nc.setAdministratorUids(new int[0]);
@@ -2311,9 +2322,7 @@
final ArrayList<NetworkStateSnapshot> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- // TODO: Consider include SUSPENDED networks, which should be considered as
- // temporary shortage of connectivity of a connected network.
- if (nai != null && nai.networkInfo.isConnected()) {
+ if (nai != null && nai.everConnected) {
// TODO (b/73321673) : NetworkStateSnapshot contains a copy of the
// NetworkCapabilities, which may contain UIDs of apps to which the
// network applies. Should the UIDs be cleared so as not to leak or
@@ -2354,6 +2363,26 @@
return false;
}
+ private int getAppUid(final String app, final UserHandle user) {
+ final PackageManager pm =
+ mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return pm.getPackageUid(app, 0 /* flags */);
+ } catch (PackageManager.NameNotFoundException e) {
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void verifyCallingUidAndPackage(String packageName, int callingUid) {
+ final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
+ if (getAppUid(packageName, user) != callingUid) {
+ throw new SecurityException(packageName + " does not belong to uid " + callingUid);
+ }
+ }
+
/**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
@@ -2369,6 +2398,7 @@
if (disallowedBecauseSystemCaller()) {
return false;
}
+ verifyCallingUidAndPackage(callingPackageName, mDeps.getCallingUid());
enforceChangePermission(callingPackageName, callingAttributionTag);
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityRestrictedNetworksPermission();
@@ -2758,6 +2788,8 @@
sendStickyBroadcast(makeGeneralIntent(info, bcastType));
}
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+ @TargetApi(Build.VERSION_CODES.S)
private void sendStickyBroadcast(Intent intent) {
synchronized (this) {
if (!mSystemReady
@@ -2803,6 +2835,9 @@
*/
@VisibleForTesting
public void systemReadyInternal() {
+ // Load flags after PackageManager is ready to query module version
+ mFlags.loadFlags(mDeps, mContext);
+
// Since mApps in PermissionMonitor needs to be populated first to ensure that
// listening network request which is sent by MultipathPolicyTracker won't be added
// NET_CAPABILITY_FOREGROUND capability. Thus, MultipathPolicyTracker.start() must
@@ -3010,9 +3045,9 @@
}
pw.println();
- pw.print("Current per-app default networks: ");
+ pw.println("Current network preferences: ");
pw.increaseIndent();
- dumpPerAppNetworkPreferences(pw);
+ dumpNetworkPreferences(pw);
pw.decreaseIndent();
pw.println();
@@ -3140,37 +3175,55 @@
}
}
- private void dumpPerAppNetworkPreferences(IndentingPrintWriter pw) {
- pw.println("Per-App Network Preference:");
- pw.increaseIndent();
- if (0 == mOemNetworkPreferences.getNetworkPreferences().size()) {
- pw.println("none");
- } else {
- pw.println(mOemNetworkPreferences.toString());
+ private void dumpNetworkPreferences(IndentingPrintWriter pw) {
+ if (!mProfileNetworkPreferences.isEmpty()) {
+ pw.println("Profile preferences:");
+ pw.increaseIndent();
+ pw.println(mProfileNetworkPreferences.preferences);
+ pw.decreaseIndent();
}
- pw.decreaseIndent();
+ if (!mOemNetworkPreferences.isEmpty()) {
+ pw.println("OEM preferences:");
+ pw.increaseIndent();
+ pw.println(mOemNetworkPreferences);
+ pw.decreaseIndent();
+ }
+ if (!mMobileDataPreferredUids.isEmpty()) {
+ pw.println("Mobile data preferred UIDs:");
+ pw.increaseIndent();
+ pw.println(mMobileDataPreferredUids);
+ pw.decreaseIndent();
+ }
+ pw.println("Default requests:");
+ pw.increaseIndent();
+ dumpPerAppDefaultRequests(pw);
+ pw.decreaseIndent();
+ }
+
+ private void dumpPerAppDefaultRequests(IndentingPrintWriter pw) {
for (final NetworkRequestInfo defaultRequest : mDefaultNetworkRequests) {
if (mDefaultRequest == defaultRequest) {
continue;
}
- final boolean isActive = null != defaultRequest.getSatisfier();
- pw.println("Is per-app network active:");
- pw.increaseIndent();
- pw.println(isActive);
- if (isActive) {
- pw.println("Active network: " + defaultRequest.getSatisfier().network.netId);
- }
- pw.println("Tracked UIDs:");
- pw.increaseIndent();
- if (0 == defaultRequest.mRequests.size()) {
- pw.println("none, this should never occur.");
+ final NetworkAgentInfo satisfier = defaultRequest.getSatisfier();
+ final String networkOutput;
+ if (null == satisfier) {
+ networkOutput = "null";
+ } else if (mNoServiceNetwork.equals(satisfier)) {
+ networkOutput = "no service network";
} else {
- pw.println(defaultRequest.mRequests.get(0).networkCapabilities.getUidRanges());
+ networkOutput = String.valueOf(satisfier.network.netId);
}
- pw.decreaseIndent();
- pw.decreaseIndent();
+ final String asUidString = (defaultRequest.mAsUid == defaultRequest.mUid)
+ ? "" : " asUid: " + defaultRequest.mAsUid;
+ final String requestInfo = "Request: [uid/pid:" + defaultRequest.mUid + "/"
+ + defaultRequest.mPid + asUidString + "]";
+ final String satisfierOutput = "Satisfier: [" + networkOutput + "]"
+ + " Preference order: " + defaultRequest.mPreferenceOrder
+ + " Tracked UIDs: " + defaultRequest.getUids();
+ pw.println(requestInfo + " - " + satisfierOutput);
}
}
@@ -3191,6 +3244,22 @@
}
}
+ private void dumpAllRequestInfoLogsToLogcat() {
+ try (PrintWriter logPw = new PrintWriter(new Writer() {
+ @Override
+ public void write(final char[] cbuf, final int off, final int len) {
+ // This method is called with 0-length and 1-length arrays for empty strings
+ // or strings containing only the DEL character.
+ if (len <= 1) return;
+ Log.e(TAG, new String(cbuf, off, len));
+ }
+ @Override public void flush() {}
+ @Override public void close() {}
+ })) {
+ mNetworkRequestInfoLogs.dump(logPw);
+ }
+ }
+
/**
* Return an array of all current NetworkAgentInfos sorted by network id.
*/
@@ -3844,9 +3913,7 @@
private void handleNetworkAgentDisconnected(Message msg) {
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
- if (mNetworkAgentInfos.contains(nai)) {
- disconnectAndDestroyNetwork(nai);
- }
+ disconnectAndDestroyNetwork(nai);
}
// Destroys a network, remove references to it from the internal state managed by
@@ -3854,6 +3921,9 @@
// Must be called on the Handler thread.
private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
ensureRunningOnConnectivityServiceThread();
+
+ if (!mNetworkAgentInfos.contains(nai)) return;
+
if (DBG) {
log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
}
@@ -3936,10 +4006,12 @@
}
// Delayed teardown.
- try {
- mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM);
- } catch (RemoteException e) {
- Log.d(TAG, "Error marking network restricted during teardown: " + e);
+ if (nai.created) {
+ try {
+ mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error marking network restricted during teardown: ", e);
+ }
}
mHandler.postDelayed(() -> destroyNetwork(nai), nai.teardownDelayMs);
}
@@ -3972,11 +4044,11 @@
config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.VIRTUAL,
INetd.PERMISSION_NONE,
(nai.networkAgentConfig == null || !nai.networkAgentConfig.allowBypass),
- getVpnType(nai));
+ getVpnType(nai), /*excludeLocalRoutes=*/ false);
} else {
config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.PHYSICAL,
getNetworkPermission(nai.networkCapabilities), /*secure=*/ false,
- VpnManager.TYPE_VPN_NONE);
+ VpnManager.TYPE_VPN_NONE, /*excludeLocalRoutes=*/ false);
}
mNetd.networkCreate(config);
mDnsResolver.createNetworkCache(nai.network.getNetId());
@@ -4016,6 +4088,20 @@
return null;
}
+ private void checkNrisConsistency(final NetworkRequestInfo nri) {
+ if (SdkLevel.isAtLeastT()) {
+ for (final NetworkRequestInfo n : mNetworkRequests.values()) {
+ if (n.mBinder != null && n.mBinder == nri.mBinder) {
+ // Temporary help to debug b/194394697 ; TODO : remove this function when the
+ // bug is fixed.
+ dumpAllRequestInfoLogsToLogcat();
+ throw new IllegalStateException("This NRI is already registered. New : " + nri
+ + ", existing : " + n);
+ }
+ }
+ }
+ }
+
private void handleRegisterNetworkRequestWithIntent(@NonNull final Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
// handleRegisterNetworkRequestWithIntent() doesn't apply to multilayer requests.
@@ -4041,6 +4127,7 @@
ensureRunningOnConnectivityServiceThread();
for (final NetworkRequestInfo nri : nris) {
mNetworkRequestInfoLogs.log("REGISTER " + nri);
+ checkNrisConsistency(nri);
for (final NetworkRequest req : nri.mRequests) {
mNetworkRequests.put(req, nri);
// TODO: Consider update signal strength for other types.
@@ -4062,7 +4149,11 @@
}
}
- rematchAllNetworksAndRequests();
+ if (mFlags.noRematchAllRequestsOnRegister()) {
+ rematchNetworksAndRequests(nris);
+ } else {
+ rematchAllNetworksAndRequests();
+ }
// Requests that have not been matched to a network will not have been sent to the
// providers, because the old satisfier and the new satisfier are the same (null in this
@@ -4277,12 +4368,11 @@
}
nri.decrementRequestCount();
mNetworkRequestInfoLogs.log("RELEASE " + nri);
+ checkNrisConsistency(nri);
if (null != nri.getActiveRequest()) {
if (!nri.getActiveRequest().isListen()) {
removeSatisfiedNetworkRequestFromNetwork(nri);
- } else {
- nri.setSatisfier(null, null);
}
}
@@ -4347,7 +4437,6 @@
} else {
wasKept = true;
}
- nri.setSatisfier(null, null);
if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
// Went from foreground to background.
updateCapabilitiesForNetwork(nai);
@@ -4960,9 +5049,10 @@
break;
}
case EVENT_SET_PROFILE_NETWORK_PREFERENCE: {
- final Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener> arg =
- (Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener>)
- msg.obj;
+ final Pair<List<ProfileNetworkPreferenceList.Preference>,
+ IOnCompleteListener> arg =
+ (Pair<List<ProfileNetworkPreferenceList.Preference>,
+ IOnCompleteListener>) msg.obj;
handleSetProfileNetworkPreference(arg.first, arg.second);
break;
}
@@ -5585,7 +5675,8 @@
private void onUserRemoved(@NonNull final UserHandle user) {
mPermissionMonitor.onUserRemoved(user);
// If there was a network preference for this user, remove it.
- handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null),
+ handleSetProfileNetworkPreference(
+ List.of(new ProfileNetworkPreferenceList.Preference(user, null, true)),
null /* listener */);
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
@@ -5917,7 +6008,12 @@
void unlinkDeathRecipient() {
if (null != mBinder) {
- mBinder.unlinkToDeath(this, 0);
+ try {
+ mBinder.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ // Temporary workaround for b/194394697 pending analysis of additional logs
+ Log.wtf(TAG, "unlinkToDeath for already unlinked NRI " + this);
+ }
}
}
@@ -5937,7 +6033,7 @@
@Override
public void binderDied() {
log("ConnectivityService NetworkRequestInfo binderDied(" +
- "uid/pid:" + mUid + "/" + mPid + ", " + mBinder + ")");
+ "uid/pid:" + mUid + "/" + mPid + ", " + mRequests + ", " + mBinder + ")");
// As an immutable collection, mRequests cannot change by the time the
// lambda is evaluated on the handler thread so calling .get() from a binder thread
// is acceptable. Use handleReleaseNetworkRequest and not directly
@@ -6045,6 +6141,8 @@
}
}
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+ @TargetApi(Build.VERSION_CODES.S)
private boolean isTargetSdkAtleast(int version, int callingUid,
@NonNull String callingPackageName) {
final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
@@ -6059,7 +6157,7 @@
@Override
public NetworkRequest requestNetwork(int asUid, NetworkCapabilities networkCapabilities,
- int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder,
+ int reqTypeInt, Messenger messenger, int timeoutMs, final IBinder binder,
int legacyType, int callbackFlags, @NonNull String callingPackageName,
@Nullable String callingAttributionTag) {
if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
@@ -6512,7 +6610,8 @@
// Current per-profile network preferences. This object follows the same threading rules as
// the OEM network preferences above.
@NonNull
- private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
+ private ProfileNetworkPreferenceList mProfileNetworkPreferences =
+ new ProfileNetworkPreferenceList();
// A set of UIDs that should use mobile data preferentially if available. This object follows
// the same threading rules as the OEM network preferences above.
@@ -6867,7 +6966,6 @@
if (DBG) log("Received offer from an unregistered provider");
return;
}
-
final NetworkOfferInfo existingOffer = findNetworkOfferInfoByCallback(newOffer.callback);
if (null != existingOffer) {
handleUnregisterNetworkOffer(existingOffer);
@@ -6897,7 +6995,11 @@
if (DBG) {
log("unregister offer from providerId " + noi.offer.providerId + " : " + noi.offer);
}
- mNetworkOffers.remove(noi);
+
+ // If the provider removes the offer and dies immediately afterwards this
+ // function may be called twice in a row, but the array will no longer contain
+ // the offer.
+ if (!mNetworkOffers.remove(noi)) return;
noi.offer.callback.asBinder().unlinkToDeath(noi, 0 /* flags */);
}
@@ -7046,14 +7148,6 @@
int mark = mResources.get().getInteger(R.integer.config_networkWakeupPacketMark);
int mask = mResources.get().getInteger(R.integer.config_networkWakeupPacketMask);
- // TODO (b/183076074): remove legacy fallback after migrating overlays
- final int legacyMark = mContext.getResources().getInteger(mContext.getResources()
- .getIdentifier("config_networkWakeupPacketMark", "integer", "android"));
- final int legacyMask = mContext.getResources().getInteger(mContext.getResources()
- .getIdentifier("config_networkWakeupPacketMask", "integer", "android"));
- mark = mark == 0 ? legacyMark : mark;
- mask = mask == 0 ? legacyMask : mask;
-
// Mask/mark of zero will not detect anything interesting.
// Don't install rules unless both values are nonzero.
if (mark == 0 || mask == 0) {
@@ -7309,7 +7403,9 @@
boolean suspended = true; // suspended if all underlying are suspended
boolean hadUnderlyingNetworks = false;
+ ArrayList<Network> newUnderlyingNetworks = null;
if (null != underlyingNetworks) {
+ newUnderlyingNetworks = new ArrayList<>();
for (Network underlyingNetwork : underlyingNetworks) {
final NetworkAgentInfo underlying =
getNetworkAgentInfoForNetwork(underlyingNetwork);
@@ -7339,6 +7435,7 @@
// If this network is not suspended, the VPN is not suspended (the VPN
// is able to transfer some data).
suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ newUnderlyingNetworks.add(underlyingNetwork);
}
}
if (!hadUnderlyingNetworks) {
@@ -7356,6 +7453,7 @@
newNc.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
newNc.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
newNc.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
+ newNc.setUnderlyingNetworks(newUnderlyingNetworks);
}
/**
@@ -7613,7 +7711,9 @@
// changed.
// TODO: Try to track the default network that apps use and only send a proxy broadcast when
// that happens to prevent false alarms.
- if (nai.isVPN() && nai.everConnected && !NetworkCapabilities.hasSameUids(prevNc, newNc)
+ final Set<UidRange> prevUids = prevNc == null ? null : prevNc.getUidRanges();
+ final Set<UidRange> newUids = newNc == null ? null : newNc.getUidRanges();
+ if (nai.isVPN() && nai.everConnected && !UidRange.hasSameUids(prevUids, newUids)
&& (nai.linkProperties.getHttpProxy() != null || isProxySetOnAnyDefaultNetwork())) {
mProxyTracker.sendProxyBroadcast();
}
@@ -7826,6 +7926,30 @@
bundle.putParcelable(t.getClass().getSimpleName(), t);
}
+ /**
+ * Returns whether reassigning a request from an NAI to another can be done gracefully.
+ *
+ * When a request should be assigned to a new network, it is normally lingered to give
+ * time for apps to gracefully migrate their connections. When both networks are on the same
+ * radio, but that radio can't do time-sharing efficiently, this may end up being
+ * counter-productive because any traffic on the old network may drastically reduce the
+ * performance of the new network.
+ * The stack supports a configuration to let modem vendors state that their radio can't
+ * do time-sharing efficiently. If this configuration is set, the stack assumes moving
+ * from one cell network to another can't be done gracefully.
+ *
+ * @param oldNai the old network serving the request
+ * @param newNai the new network serving the request
+ * @return whether the switch can be graceful
+ */
+ private boolean canSupportGracefulNetworkSwitch(@NonNull final NetworkAgentInfo oldSatisfier,
+ @NonNull final NetworkAgentInfo newSatisfier) {
+ if (mCellularRadioTimesharingCapable) return true;
+ return !oldSatisfier.networkCapabilities.hasSingleTransport(TRANSPORT_CELLULAR)
+ || !newSatisfier.networkCapabilities.hasSingleTransport(TRANSPORT_CELLULAR)
+ || !newSatisfier.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY);
+ }
+
private void teardownUnneededNetwork(NetworkAgentInfo nai) {
if (nai.numRequestNetworkRequests() != 0) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
@@ -8086,7 +8210,13 @@
log(" accepting network in place of " + previousSatisfier.toShortString());
}
previousSatisfier.removeRequest(previousRequest.requestId);
- previousSatisfier.lingerRequest(previousRequest.requestId, now);
+ if (canSupportGracefulNetworkSwitch(previousSatisfier, newSatisfier)) {
+ // If this network switch can't be supported gracefully, the request is not
+ // lingered. This allows letting go of the network sooner to reclaim some
+ // performance on the new network, since the radio can't do both at the same
+ // time while preserving good performance.
+ previousSatisfier.lingerRequest(previousRequest.requestId, now);
+ }
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
@@ -9979,19 +10109,26 @@
* See the documentation for the individual preferences for a description of the supported
* behaviors.
*
- * @param profile the profile concerned.
- * @param preference the preference for this profile, as one of the PROFILE_NETWORK_PREFERENCE_*
- * constants.
+ * @param profile the user profile for whih the preference is being set.
+ * @param preferences the list of profile network preferences for the
+ * provided profile.
* @param listener an optional listener to listen for completion of the operation.
*/
@Override
- public void setProfileNetworkPreference(@NonNull final UserHandle profile,
- @ConnectivityManager.ProfileNetworkPreference final int preference,
+ public void setProfileNetworkPreferences(
+ @NonNull final UserHandle profile,
+ @NonNull List<ProfileNetworkPreference> preferences,
@Nullable final IOnCompleteListener listener) {
+ Objects.requireNonNull(preferences);
Objects.requireNonNull(profile);
+
+ if (preferences.size() == 0) {
+ preferences.add((new ProfileNetworkPreference.Builder()).build());
+ }
+
PermissionUtils.enforceNetworkStackPermission(mContext);
if (DBG) {
- log("setProfileNetworkPreference " + profile + " to " + preference);
+ log("setProfileNetworkPreferences " + profile + " to " + preferences);
}
if (profile.getIdentifier() < 0) {
throw new IllegalArgumentException("Must explicitly specify a user handle ("
@@ -10002,23 +10139,68 @@
throw new IllegalArgumentException("Profile must be a managed profile");
}
- final NetworkCapabilities nc;
- switch (preference) {
- case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT:
- nc = null;
- break;
- case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
- final UidRange uids = UidRange.createForUser(profile);
- nc = createDefaultNetworkCapabilitiesForUidRange(uids);
- nc.addCapability(NET_CAPABILITY_ENTERPRISE);
- nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
- break;
- default:
- throw new IllegalArgumentException(
- "Invalid preference in setProfileNetworkPreference");
+ final List<ProfileNetworkPreferenceList.Preference> preferenceList =
+ new ArrayList<ProfileNetworkPreferenceList.Preference>();
+ boolean allowFallback = true;
+ for (final ProfileNetworkPreference preference : preferences) {
+ final NetworkCapabilities nc;
+ switch (preference.getPreference()) {
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT:
+ nc = null;
+ break;
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK:
+ allowFallback = false;
+ // continue to process the enterprise preference.
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
+ final Set<UidRange> uidRangeSet =
+ getUidListToBeAppliedForNetworkPreference(profile, preference);
+ if (!isRangeAlreadyInPreferenceList(preferenceList, uidRangeSet)) {
+ nc = createDefaultNetworkCapabilitiesForUidRangeSet(uidRangeSet);
+ } else {
+ throw new IllegalArgumentException(
+ "Overlapping uid range in setProfileNetworkPreferences");
+ }
+ nc.addCapability(NET_CAPABILITY_ENTERPRISE);
+ nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid preference in setProfileNetworkPreferences");
+ }
+ preferenceList.add(new ProfileNetworkPreferenceList.Preference(
+ profile, nc, allowFallback));
}
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE,
- new Pair<>(new ProfileNetworkPreferences.Preference(profile, nc), listener)));
+ new Pair<>(preferenceList, listener)));
+ }
+
+ private Set<UidRange> getUidListToBeAppliedForNetworkPreference(
+ @NonNull final UserHandle profile,
+ @NonNull final ProfileNetworkPreference profileNetworkPreference) {
+ final UidRange profileUids = UidRange.createForUser(profile);
+ Set<UidRange> uidRangeSet = UidRangeUtils.convertListToUidRange(
+ profileNetworkPreference.getIncludedUids());
+ if (uidRangeSet.size() > 0) {
+ if (!UidRangeUtils.isRangeSetInUidRange(profileUids, uidRangeSet)) {
+ throw new IllegalArgumentException(
+ "Allow uid range is outside the uid range of profile.");
+ }
+ } else {
+ ArraySet<UidRange> disallowUidRangeSet = UidRangeUtils.convertListToUidRange(
+ profileNetworkPreference.getExcludedUids());
+ if (disallowUidRangeSet.size() > 0) {
+ if (!UidRangeUtils.isRangeSetInUidRange(profileUids, disallowUidRangeSet)) {
+ throw new IllegalArgumentException(
+ "disallow uid range is outside the uid range of profile.");
+ }
+ uidRangeSet = UidRangeUtils.removeRangeSetFromUidRange(profileUids,
+ disallowUidRangeSet);
+ } else {
+ uidRangeSet = new ArraySet<UidRange>();
+ uidRangeSet.add(profileUids);
+ }
+ }
+ return uidRangeSet;
}
private void validateNetworkCapabilitiesOfProfileNetworkPreference(
@@ -10028,20 +10210,25 @@
}
private ArraySet<NetworkRequestInfo> createNrisFromProfileNetworkPreferences(
- @NonNull final ProfileNetworkPreferences prefs) {
+ @NonNull final ProfileNetworkPreferenceList prefs) {
final ArraySet<NetworkRequestInfo> result = new ArraySet<>();
- for (final ProfileNetworkPreferences.Preference pref : prefs.preferences) {
- // The NRI for a user should be comprised of two layers:
- // - The request for the capabilities
- // - The request for the default network, for fallback. Create an image of it to
- // have the correct UIDs in it (also a request can only be part of one NRI, because
- // of lookups in 1:1 associations like mNetworkRequests).
- // Note that denying a fallback can be implemented simply by not adding the second
- // request.
+ for (final ProfileNetworkPreferenceList.Preference pref : prefs.preferences) {
+ // The NRI for a user should contain the request for capabilities.
+ // If fallback to default network is needed then NRI should include
+ // the request for the default network. Create an image of it to
+ // have the correct UIDs in it (also a request can only be part of one NRI, because
+ // of lookups in 1:1 associations like mNetworkRequests).
final ArrayList<NetworkRequest> nrs = new ArrayList<>();
nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities));
- nrs.add(createDefaultInternetRequestForTransport(
- TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
+ if (pref.allowFallback) {
+ nrs.add(createDefaultInternetRequestForTransport(
+ TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
+ }
+ if (VDBG) {
+ loge("pref.capabilities.getUids():" + UidRange.fromIntRanges(
+ pref.capabilities.getUids()));
+ }
+
setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids()));
final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs,
PREFERENCE_ORDER_PROFILE);
@@ -10050,19 +10237,35 @@
return result;
}
- private void handleSetProfileNetworkPreference(
- @NonNull final ProfileNetworkPreferences.Preference preference,
- @Nullable final IOnCompleteListener listener) {
- validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
+ /**
+ * Compare if the given UID range sets have the same UIDs.
+ *
+ */
+ private boolean isRangeAlreadyInPreferenceList(
+ @NonNull List<ProfileNetworkPreferenceList.Preference> preferenceList,
+ @NonNull Set<UidRange> uidRangeSet) {
+ if (uidRangeSet.size() == 0 || preferenceList.size() == 0) {
+ return false;
+ }
+ for (ProfileNetworkPreferenceList.Preference pref : preferenceList) {
+ if (UidRangeUtils.doesRangeSetOverlap(
+ UidRange.fromIntRanges(pref.capabilities.getUids()), uidRangeSet)) {
+ return true;
+ }
+ }
+ return false;
+ }
- mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
- mSystemNetworkRequestCounter.transact(
- mDeps.getCallingUid(), mProfileNetworkPreferences.preferences.size(),
- () -> {
- final ArraySet<NetworkRequestInfo> nris =
- createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences);
- replaceDefaultNetworkRequestsForPreference(nris, PREFERENCE_ORDER_PROFILE);
- });
+ private void handleSetProfileNetworkPreference(
+ @NonNull final List<ProfileNetworkPreferenceList.Preference> preferenceList,
+ @Nullable final IOnCompleteListener listener) {
+ for (final ProfileNetworkPreferenceList.Preference preference : preferenceList) {
+ validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
+ mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
+ }
+ removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_PROFILE);
+ addPerAppDefaultNetworkRequests(
+ createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences));
// Finally, rematch.
rematchAllNetworksAndRequests();
@@ -10107,25 +10310,16 @@
private void handleMobileDataPreferredUidsChanged() {
mMobileDataPreferredUids = ConnectivitySettingsManager.getMobileDataPreferredUids(mContext);
- mSystemNetworkRequestCounter.transact(
- mDeps.getCallingUid(), 1 /* numOfNewRequests */,
- () -> {
- final ArraySet<NetworkRequestInfo> nris =
- createNrisFromMobileDataPreferredUids(mMobileDataPreferredUids);
- replaceDefaultNetworkRequestsForPreference(nris,
- PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED);
- });
+ removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED);
+ addPerAppDefaultNetworkRequests(
+ createNrisFromMobileDataPreferredUids(mMobileDataPreferredUids));
// Finally, rematch.
rematchAllNetworksAndRequests();
}
private void enforceAutomotiveDevice() {
- final boolean isAutomotiveDevice =
- mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
- if (!isAutomotiveDevice) {
- throw new UnsupportedOperationException(
- "setOemNetworkPreference() is only available on automotive devices.");
- }
+ PermissionUtils.enforceSystemFeature(mContext, PackageManager.FEATURE_AUTOMOTIVE,
+ "setOemNetworkPreference() is only available on automotive devices.");
}
/**
@@ -10203,16 +10397,9 @@
}
mOemNetworkPreferencesLogs.log("UPDATE INITIATED: " + preference);
- final int uniquePreferenceCount = new ArraySet<>(
- preference.getNetworkPreferences().values()).size();
- mSystemNetworkRequestCounter.transact(
- mDeps.getCallingUid(), uniquePreferenceCount,
- () -> {
- final ArraySet<NetworkRequestInfo> nris =
- new OemNetworkRequestFactory()
- .createNrisFromOemNetworkPreferences(preference);
- replaceDefaultNetworkRequestsForPreference(nris, PREFERENCE_ORDER_OEM);
- });
+ removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_OEM);
+ addPerAppDefaultNetworkRequests(new OemNetworkRequestFactory()
+ .createNrisFromOemNetworkPreferences(preference));
mOemNetworkPreferences = preference;
if (null != listener) {
@@ -10224,14 +10411,12 @@
}
}
- private void replaceDefaultNetworkRequestsForPreference(
- @NonNull final Set<NetworkRequestInfo> nris, final int preferenceOrder) {
+ private void removeDefaultNetworkRequestsForPreference(final int preferenceOrder) {
// Skip the requests which are set by other network preference. Because the uid range rules
// should stay in netd.
final Set<NetworkRequestInfo> requests = new ArraySet<>(mDefaultNetworkRequests);
requests.removeIf(request -> request.mPreferenceOrder != preferenceOrder);
handleRemoveNetworkRequests(requests);
- addPerAppDefaultNetworkRequests(nris);
}
private void addPerAppDefaultNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
@@ -10240,14 +10425,10 @@
final ArraySet<NetworkRequestInfo> perAppCallbackRequestsToUpdate =
getPerAppCallbackRequestsToUpdate();
final ArraySet<NetworkRequestInfo> nrisToRegister = new ArraySet<>(nris);
- mSystemNetworkRequestCounter.transact(
- mDeps.getCallingUid(), perAppCallbackRequestsToUpdate.size(),
- () -> {
- nrisToRegister.addAll(
- createPerAppCallbackRequestsToRegister(perAppCallbackRequestsToUpdate));
- handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
- handleRegisterNetworkRequests(nrisToRegister);
- });
+ handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
+ nrisToRegister.addAll(
+ createPerAppCallbackRequestsToRegister(perAppCallbackRequestsToUpdate));
+ handleRegisterNetworkRequests(nrisToRegister);
}
/**
@@ -10464,4 +10645,94 @@
return createNetworkRequest(NetworkRequest.Type.REQUEST, netcap);
}
}
+
+ @Override
+ public void updateMeteredNetworkAllowList(final int uid, final boolean add) {
+ enforceNetworkStackOrSettingsPermission();
+
+ try {
+ if (add) {
+ mNetd.bandwidthAddNiceApp(uid);
+ } else {
+ mNetd.bandwidthRemoveNiceApp(uid);
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public void updateMeteredNetworkDenyList(final int uid, final boolean add) {
+ enforceNetworkStackOrSettingsPermission();
+
+ try {
+ if (add) {
+ mNetd.bandwidthAddNaughtyApp(uid);
+ } else {
+ mNetd.bandwidthRemoveNaughtyApp(uid);
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public void updateFirewallRule(final int chain, final int uid, final boolean allow) {
+ enforceNetworkStackOrSettingsPermission();
+
+ try {
+ mNetd.firewallSetUidRule(chain, uid,
+ allow ? INetd.FIREWALL_RULE_ALLOW : INetd.FIREWALL_RULE_DENY);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public void setFirewallChainEnabled(final int chain, final boolean enable) {
+ enforceNetworkStackOrSettingsPermission();
+
+ try {
+ mNetd.firewallEnableChildChain(chain, enable);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public void replaceFirewallChain(final int chain, final int[] uids) {
+ enforceNetworkStackOrSettingsPermission();
+
+ try {
+ switch (chain) {
+ case ConnectivityManager.FIREWALL_CHAIN_DOZABLE:
+ mNetd.firewallReplaceUidChain("fw_dozable", true /* isAllowList */, uids);
+ break;
+ case ConnectivityManager.FIREWALL_CHAIN_STANDBY:
+ mNetd.firewallReplaceUidChain("fw_standby", false /* isAllowList */, uids);
+ break;
+ case ConnectivityManager.FIREWALL_CHAIN_POWERSAVE:
+ mNetd.firewallReplaceUidChain("fw_powersave", true /* isAllowList */, uids);
+ break;
+ case ConnectivityManager.FIREWALL_CHAIN_RESTRICTED:
+ mNetd.firewallReplaceUidChain("fw_restricted", true /* isAllowList */, uids);
+ break;
+ default:
+ throw new IllegalArgumentException("replaceFirewallChain with invalid chain: "
+ + chain);
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public void swapActiveStatsMap() {
+ enforceNetworkStackOrSettingsPermission();
+ try {
+ mNetd.trafficSwapActiveStatsMap();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
+ }
+ }
}
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
new file mode 100644
index 0000000..4d243c4
--- /dev/null
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static android.net.INetd.IF_STATE_UP;
+import static android.net.INetd.PERMISSION_SYSTEM;
+
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.INetd;
+import android.net.InterfaceConfigurationParcel;
+import android.net.IpPrefix;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.InterfaceParams;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * This coordinator is responsible for providing clat relevant functionality.
+ *
+ * {@hide}
+ */
+public class ClatCoordinator {
+ private static final String TAG = ClatCoordinator.class.getSimpleName();
+
+ // Sync from external/android-clat/clatd.c
+ // 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header.
+ @VisibleForTesting
+ static final int MTU_DELTA = 28;
+ @VisibleForTesting
+ static final int CLAT_MAX_MTU = 65536;
+
+ // This must match the interface prefix in clatd.c.
+ private static final String CLAT_PREFIX = "v4-";
+
+ // For historical reasons, start with 192.0.0.4, and after that, use all subsequent addresses
+ // in 192.0.0.0/29 (RFC 7335).
+ @VisibleForTesting
+ static final String INIT_V4ADDR_STRING = "192.0.0.4";
+ @VisibleForTesting
+ static final int INIT_V4ADDR_PREFIX_LEN = 29;
+ private static final InetAddress GOOGLE_DNS_4 = InetAddress.parseNumericAddress("8.8.8.8");
+
+ private static final int INVALID_IFINDEX = 0;
+ private static final int INVALID_PID = 0;
+
+ @NonNull
+ private final INetd mNetd;
+ @NonNull
+ private final Dependencies mDeps;
+ @Nullable
+ private String mIface = null;
+ private int mPid = INVALID_PID;
+
+ @VisibleForTesting
+ abstract static class Dependencies {
+ /**
+ * Get netd.
+ */
+ @NonNull
+ public abstract INetd getNetd();
+
+ /**
+ * @see ParcelFileDescriptor#adoptFd(int).
+ */
+ @NonNull
+ public ParcelFileDescriptor adoptFd(int fd) {
+ return ParcelFileDescriptor.adoptFd(fd);
+ }
+
+ /**
+ * Get interface index for a given interface.
+ */
+ public int getInterfaceIndex(String ifName) {
+ final InterfaceParams params = InterfaceParams.getByName(ifName);
+ return params != null ? params.index : INVALID_IFINDEX;
+ }
+
+ /**
+ * Create tun interface for a given interface name.
+ */
+ public int createTunInterface(@NonNull String tuniface) throws IOException {
+ return native_createTunInterface(tuniface);
+ }
+
+ /**
+ * Pick an IPv4 address for clat.
+ */
+ @NonNull
+ public String selectIpv4Address(@NonNull String v4addr, int prefixlen)
+ throws IOException {
+ return native_selectIpv4Address(v4addr, prefixlen);
+ }
+
+ /**
+ * Generate a checksum-neutral IID.
+ */
+ @NonNull
+ public String generateIpv6Address(@NonNull String iface, @NonNull String v4,
+ @NonNull String prefix64) throws IOException {
+ return native_generateIpv6Address(iface, v4, prefix64);
+ }
+
+ /**
+ * Detect MTU.
+ */
+ public int detectMtu(@NonNull String platSubnet, int platSuffix, int mark)
+ throws IOException {
+ return native_detectMtu(platSubnet, platSuffix, mark);
+ }
+
+ /**
+ * Open packet socket.
+ */
+ public int openPacketSocket() throws IOException {
+ return native_openPacketSocket();
+ }
+
+ /**
+ * Open IPv6 raw socket and set SO_MARK.
+ */
+ public int openRawSocket6(int mark) throws IOException {
+ return native_openRawSocket6(mark);
+ }
+
+ /**
+ * Add anycast setsockopt.
+ */
+ public void addAnycastSetsockopt(@NonNull FileDescriptor sock, String v6, int ifindex)
+ throws IOException {
+ native_addAnycastSetsockopt(sock, v6, ifindex);
+ }
+
+ /**
+ * Configure packet socket.
+ */
+ public void configurePacketSocket(@NonNull FileDescriptor sock, String v6, int ifindex)
+ throws IOException {
+ native_configurePacketSocket(sock, v6, ifindex);
+ }
+ }
+
+ @VisibleForTesting
+ static int getFwmark(int netId) {
+ // See union Fwmark in system/netd/include/Fwmark.h
+ return (netId & 0xffff)
+ | 0x1 << 16 // protectedFromVpn: true
+ | 0x1 << 17 // explicitlySelected: true
+ | (PERMISSION_SYSTEM & 0x3) << 18;
+ }
+
+ @VisibleForTesting
+ static int adjustMtu(int mtu) {
+ // clamp to minimum ipv6 mtu - this probably cannot ever trigger
+ if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU;
+ // clamp to buffer size
+ if (mtu > CLAT_MAX_MTU) mtu = CLAT_MAX_MTU;
+ // decrease by ipv6(40) + ipv6 fragmentation header(8) vs ipv4(20) overhead of 28 bytes
+ mtu -= MTU_DELTA;
+
+ return mtu;
+ }
+
+ public ClatCoordinator(@NonNull Dependencies deps) {
+ mDeps = deps;
+ mNetd = mDeps.getNetd();
+ }
+
+ /**
+ * Start clatd for a given interface and NAT64 prefix.
+ */
+ public String clatStart(final String iface, final int netId,
+ @NonNull final IpPrefix nat64Prefix)
+ throws IOException {
+ if (nat64Prefix.getPrefixLength() != 96) {
+ throw new IOException("Prefix must be 96 bits long: " + nat64Prefix);
+ }
+
+ // [1] Pick an IPv4 address from 192.0.0.4, 192.0.0.5, 192.0.0.6 ..
+ final String v4;
+ try {
+ v4 = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN);
+ } catch (IOException e) {
+ throw new IOException("no IPv4 addresses were available for clat: " + e);
+ }
+
+ // [2] Generate a checksum-neutral IID.
+ final String pfx96 = nat64Prefix.getAddress().getHostAddress();
+ final String v6;
+ try {
+ v6 = mDeps.generateIpv6Address(iface, v4, pfx96);
+ } catch (IOException e) {
+ throw new IOException("no IPv6 addresses were available for clat: " + e);
+ }
+
+ // [3] Open, configure and bring up the tun interface.
+ // Create the v4-... tun interface.
+ final String tunIface = CLAT_PREFIX + iface;
+ final ParcelFileDescriptor tunFd;
+ try {
+ tunFd = mDeps.adoptFd(mDeps.createTunInterface(tunIface));
+ } catch (IOException e) {
+ throw new IOException("Create tun interface " + tunIface + " failed: " + e);
+ }
+
+ // disable IPv6 on it - failing to do so is not a critical error
+ try {
+ mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Disable IPv6 on " + tunIface + " failed: " + e);
+ }
+
+ // Detect ipv4 mtu.
+ final Integer fwmark = getFwmark(netId);
+ final int detectedMtu = mDeps.detectMtu(pfx96,
+ ByteBuffer.wrap(GOOGLE_DNS_4.getAddress()).getInt(), fwmark);
+ final int mtu = adjustMtu(detectedMtu);
+ Log.i(TAG, "ipv4 mtu is " + mtu);
+
+ // TODO: add setIptablesDropRule
+
+ // Config tun interface mtu, address and bring up.
+ try {
+ mNetd.interfaceSetMtu(tunIface, mtu);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IOException("Set MTU " + mtu + " on " + tunIface + " failed: " + e);
+ }
+ final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
+ ifConfig.ifName = tunIface;
+ ifConfig.ipv4Addr = v4;
+ ifConfig.prefixLength = 32;
+ ifConfig.hwAddr = "";
+ ifConfig.flags = new String[] {IF_STATE_UP};
+ try {
+ mNetd.interfaceSetCfg(ifConfig);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IOException("Setting IPv4 address to " + ifConfig.ipv4Addr + "/"
+ + ifConfig.prefixLength + " failed on " + ifConfig.ifName + ": " + e);
+ }
+
+ // [4] Open and configure local 464xlat read/write sockets.
+ // Opens a packet socket to receive IPv6 packets in clatd.
+ final ParcelFileDescriptor readSock6;
+ try {
+ // Use a JNI call to get native file descriptor instead of Os.socket() because we would
+ // like to use ParcelFileDescriptor to close file descriptor automatically. But ctor
+ // ParcelFileDescriptor(FileDescriptor fd) is a @hide function. Need to use native file
+ // descriptor to initialize ParcelFileDescriptor object instead.
+ readSock6 = mDeps.adoptFd(mDeps.openPacketSocket());
+ } catch (IOException e) {
+ throw new IOException("Open packet socket failed: " + e);
+ }
+
+ // Opens a raw socket with a given fwmark to send IPv6 packets in clatd.
+ final ParcelFileDescriptor writeSock6;
+ try {
+ // Use a JNI call to get native file descriptor instead of Os.socket(). See above
+ // reason why we use jniOpenPacketSocket6().
+ writeSock6 = mDeps.adoptFd(mDeps.openRawSocket6(fwmark));
+ } catch (IOException e) {
+ throw new IOException("Open raw socket failed: " + e);
+ }
+
+ final int ifaceIndex = mDeps.getInterfaceIndex(iface);
+ if (ifaceIndex == INVALID_IFINDEX) {
+ throw new IOException("Fail to get interface index for interface " + iface);
+ }
+
+ // Start translating packets to the new prefix.
+ try {
+ mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6, ifaceIndex);
+ } catch (IOException e) {
+ throw new IOException("add anycast sockopt failed: " + e);
+ }
+
+ // Update our packet socket filter to reflect the new 464xlat IP address.
+ try {
+ mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6, ifaceIndex);
+ } catch (IOException e) {
+ throw new IOException("configure packet socket failed: " + e);
+ }
+
+ // TODO: start clatd and returns local xlat464 v6 address.
+ return null;
+ }
+
+ private static native String native_selectIpv4Address(String v4addr, int prefixlen)
+ throws IOException;
+ private static native String native_generateIpv6Address(String iface, String v4,
+ String prefix64) throws IOException;
+ private static native int native_createTunInterface(String tuniface) throws IOException;
+ private static native int native_detectMtu(String platSubnet, int platSuffix, int mark)
+ throws IOException;
+ private static native int native_openPacketSocket() throws IOException;
+ private static native int native_openRawSocket6(int mark) throws IOException;
+ private static native void native_addAnycastSetsockopt(FileDescriptor sock, String v6,
+ int ifindex) throws IOException;
+ private static native void native_configurePacketSocket(FileDescriptor sock, String v6,
+ int ifindex) throws IOException;
+}
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
new file mode 100644
index 0000000..122ea1c
--- /dev/null
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ConnectivityService;
+
+/**
+ * Collection of constants for the connectivity module.
+ */
+public final class ConnectivityFlags {
+ /**
+ * Minimum module version at which to avoid rematching all requests when a network request is
+ * registered, and rematch only the registered requests instead.
+ */
+ @VisibleForTesting
+ public static final String NO_REMATCH_ALL_REQUESTS_ON_REGISTER =
+ "no_rematch_all_requests_on_register";
+
+ private boolean mNoRematchAllRequestsOnRegister;
+
+ /**
+ * Whether ConnectivityService should avoid avoid rematching all requests when a network
+ * request is registered, and rematch only the registered requests instead.
+ *
+ * This flag is disabled by default.
+ *
+ * IMPORTANT NOTE: This flag is false by default and will only be loaded in ConnectivityService
+ * systemReady. It is also not volatile for performance reasons, so for most threads it may
+ * only change to true after some time. This is fine for this particular flag because it only
+ * controls whether all requests or a subset of requests should be rematched, which is only
+ * a performance optimization, so its value does not need to be consistent over time; but most
+ * flags will not have these properties and should not use the same model.
+ *
+ * TODO: when adding other flags, consider the appropriate timing to load them, and necessary
+ * threading guarantees according to the semantics of the flags.
+ */
+ public boolean noRematchAllRequestsOnRegister() {
+ return mNoRematchAllRequestsOnRegister;
+ }
+
+ /**
+ * Load flag values. Should only be called once, and can only be called once PackageManager is
+ * ready.
+ */
+ public void loadFlags(ConnectivityService.Dependencies deps, Context ctx) {
+ mNoRematchAllRequestsOnRegister = deps.isFeatureEnabled(
+ ctx, NO_REMATCH_ALL_REQUESTS_ON_REGISTER, false /* defaultEnabled */);
+ }
+}
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index ee1538a..3b58823 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -114,18 +114,11 @@
mContext = context;
mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext);
- // TODO (b/183076074): stop reading legacy resources after migrating overlays
- final int legacyReservedSlots = mContext.getResources().getInteger(
- mContext.getResources().getIdentifier(
- "config_reservedPrivilegedKeepaliveSlots", "integer", "android"));
- final int legacyAllowedSlots = mContext.getResources().getInteger(
- mContext.getResources().getIdentifier(
- "config_allowedUnprivilegedKeepalivePerUid", "integer", "android"));
final ConnectivityResources res = new ConnectivityResources(mContext);
- mReservedPrivilegedSlots = Math.min(legacyReservedSlots, res.get().getInteger(
- R.integer.config_reservedPrivilegedKeepaliveSlots));
- mAllowedUnprivilegedSlotsForUid = Math.min(legacyAllowedSlots, res.get().getInteger(
- R.integer.config_allowedUnprivilegedKeepalivePerUid));
+ mReservedPrivilegedSlots = res.get().getInteger(
+ R.integer.config_reservedPrivilegedKeepaliveSlots);
+ mAllowedUnprivilegedSlotsForUid = res.get().getInteger(
+ R.integer.config_allowedUnprivilegedKeepalivePerUid);
}
/**
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 6426f86..b7f3ed9 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -377,6 +377,9 @@
this.creatorUid = creatorUid;
mLingerDurationMs = lingerDurationMs;
mQosCallbackTracker = qosCallbackTracker;
+ declaredUnderlyingNetworks = (nc.getUnderlyingNetworks() != null)
+ ? nc.getUnderlyingNetworks().toArray(new Network[0])
+ : null;
}
private class AgentDeathMonitor implements IBinder.DeathRecipient {
diff --git a/service/src/com/android/server/connectivity/NetworkRanker.java b/service/src/com/android/server/connectivity/NetworkRanker.java
index d7eb9c8..43da1d0 100644
--- a/service/src/com/android/server/connectivity/NetworkRanker.java
+++ b/service/src/com/android/server/connectivity/NetworkRanker.java
@@ -63,8 +63,6 @@
NetworkCapabilities getCapsNoCopy();
}
- private static final boolean USE_POLICY_RANKING = true;
-
public NetworkRanker() { }
/**
@@ -77,11 +75,7 @@
final ArrayList<NetworkAgentInfo> candidates = filter(nais, nai -> nai.satisfies(request));
if (candidates.size() == 1) return candidates.get(0); // Only one potential satisfier
if (candidates.size() <= 0) return null; // No network can satisfy this request
- if (USE_POLICY_RANKING) {
- return getBestNetworkByPolicy(candidates, currentSatisfier);
- } else {
- return getBestNetworkByLegacyInt(candidates);
- }
+ return getBestNetworkByPolicy(candidates, currentSatisfier);
}
// Transport preference order, if it comes down to that.
@@ -278,23 +272,6 @@
return candidates.get(0);
}
- // TODO : switch to the policy implementation and remove
- // Almost equivalent to Collections.max(nais), but allows returning null if no network
- // satisfies the request.
- private NetworkAgentInfo getBestNetworkByLegacyInt(
- @NonNull final Collection<NetworkAgentInfo> nais) {
- NetworkAgentInfo bestNetwork = null;
- int bestScore = Integer.MIN_VALUE;
- for (final NetworkAgentInfo nai : nais) {
- final int naiScore = nai.getCurrentScore();
- if (naiScore > bestScore) {
- bestNetwork = nai;
- bestScore = naiScore;
- }
- }
- return bestNetwork;
- }
-
/**
* Returns whether a {@link Scoreable} has a chance to beat a champion network for a request.
*
@@ -322,30 +299,11 @@
// If there is no satisfying network, then this network can beat, because some network
// is always better than no network.
if (null == champion) return true;
- if (USE_POLICY_RANKING) {
- // If there is no champion, the offer can always beat.
- // Otherwise rank them.
- final ArrayList<Scoreable> candidates = new ArrayList<>();
- candidates.add(champion);
- candidates.add(contestant);
- return contestant == getBestNetworkByPolicy(candidates, champion);
- } else {
- return mightBeatByLegacyInt(champion.getScore(), contestant);
- }
- }
-
- /**
- * Returns whether a contestant might beat a champion according to the legacy int.
- */
- private boolean mightBeatByLegacyInt(@Nullable final FullScore championScore,
- @NonNull final Scoreable contestant) {
- final int offerIntScore;
- if (contestant.getCapsNoCopy().hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- // If the offer might have Internet access, then it might validate.
- offerIntScore = contestant.getScore().getLegacyIntAsValidated();
- } else {
- offerIntScore = contestant.getScore().getLegacyInt();
- }
- return championScore.getLegacyInt() < offerIntScore;
+ // If there is no champion, the offer can always beat.
+ // Otherwise rank them.
+ final ArrayList<Scoreable> candidates = new ArrayList<>();
+ candidates.add(champion);
+ candidates.add(contestant);
+ return contestant == getBestNetworkByPolicy(candidates, champion);
}
}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index f596c4a..439db89 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -23,7 +23,6 @@
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
import static android.net.INetd.PERMISSION_INTERNET;
import static android.net.INetd.PERMISSION_NETWORK;
@@ -51,6 +50,7 @@
import android.net.INetd;
import android.net.UidRange;
import android.net.Uri;
+import android.net.util.SharedLog;
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -59,9 +59,9 @@
import android.os.UserManager;
import android.provider.Settings;
import android.system.OsConstants;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
@@ -97,9 +97,9 @@
@GuardedBy("this")
private final Set<UserHandle> mUsers = new HashSet<>();
- // Keys are appIds. Values are true for SYSTEM permission and false for NETWORK permission.
+ // Keys are uids. Values are netd network permissions.
@GuardedBy("this")
- private final SparseIntArray mApps = new SparseIntArray();
+ private final SparseIntArray mUidToNetworkPerm = new SparseIntArray();
// Keys are active non-bypassable and fully-routed VPN's interface name, Values are uid ranges
// for apps under the VPN
@@ -120,6 +120,14 @@
@GuardedBy("this")
private final Set<Integer> mUidsAllowedOnRestrictedNetworks = new ArraySet<>();
+ @GuardedBy("this")
+ private final Map<UserHandle, PackageManager> mUsersPackageManager = new ArrayMap<>();
+
+ private static final int SYSTEM_APPID = SYSTEM_UID;
+
+ private static final int MAX_PERMISSION_UPDATE_LOGS = 40;
+ private final SharedLog mPermissionUpdateLogs = new SharedLog(MAX_PERMISSION_UPDATE_LOGS, TAG);
+
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -208,6 +216,83 @@
return targetPermission > currentPermission;
}
+ private List<PackageInfo> getInstalledPackagesAsUser(final UserHandle user) {
+ return mPackageManager.getInstalledPackagesAsUser(GET_PERMISSIONS, user.getIdentifier());
+ }
+
+ private synchronized void updateAllApps(final List<PackageInfo> apps) {
+ for (PackageInfo app : apps) {
+ final int appId = app.applicationInfo != null
+ ? UserHandle.getAppId(app.applicationInfo.uid) : INVALID_UID;
+ if (appId < 0) {
+ continue;
+ }
+ mAllApps.add(appId);
+ }
+ }
+
+ // Return the network permission for the passed list of apps. Note that this depends on the
+ // current settings of the device (See isUidAllowedOnRestrictedNetworks).
+ private SparseIntArray makeUidsNetworkPerm(final List<PackageInfo> apps) {
+ final SparseIntArray uidsPerm = new SparseIntArray();
+ for (PackageInfo app : apps) {
+ final int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
+ if (uid < 0) {
+ continue;
+ }
+ final int permission = getPackageNetdNetworkPermission(app);
+ if (isHigherNetworkPermission(permission, uidsPerm.get(uid, PERMISSION_NONE))) {
+ uidsPerm.put(uid, permission);
+ }
+ }
+ return uidsPerm;
+ }
+
+ private static SparseIntArray makeAppIdsTrafficPerm(final List<PackageInfo> apps) {
+ final SparseIntArray appIdsPerm = new SparseIntArray();
+ for (PackageInfo app : apps) {
+ final int appId = app.applicationInfo != null
+ ? UserHandle.getAppId(app.applicationInfo.uid) : INVALID_UID;
+ if (appId < 0) {
+ continue;
+ }
+ final int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
+ app.requestedPermissionsFlags);
+ appIdsPerm.put(appId, appIdsPerm.get(appId) | otherNetdPerms);
+ }
+ return appIdsPerm;
+ }
+
+ private synchronized void updateUidsNetworkPermission(final SparseIntArray uids) {
+ for (int i = 0; i < uids.size(); i++) {
+ mUidToNetworkPerm.put(uids.keyAt(i), uids.valueAt(i));
+ }
+ sendUidsNetworkPermission(uids, true /* add */);
+ }
+
+ private void updateAppIdsTrafficPermission(final SparseIntArray appIds,
+ final SparseIntArray extraAppIds) {
+ for (int i = 0; i < extraAppIds.size(); i++) {
+ final int appId = extraAppIds.keyAt(i);
+ final int permission = extraAppIds.valueAt(i);
+ appIds.put(appId, appIds.get(appId) | permission);
+ }
+ sendAppIdsTrafficPermission(appIds);
+ }
+
+ private SparseIntArray getSystemTrafficPerm() {
+ final SparseIntArray appIdsPerm = new SparseIntArray();
+ for (final int uid : mSystemConfigManager.getSystemPermissionUids(INTERNET)) {
+ final int appId = UserHandle.getAppId(uid);
+ appIdsPerm.put(appId, appIdsPerm.get(appId) | PERMISSION_INTERNET);
+ }
+ for (final int uid : mSystemConfigManager.getSystemPermissionUids(UPDATE_DEVICE_STATS)) {
+ final int appId = UserHandle.getAppId(uid);
+ appIdsPerm.put(appId, appIdsPerm.get(appId) | PERMISSION_UPDATE_DEVICE_STATS);
+ }
+ return appIdsPerm;
+ }
+
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
// receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
public synchronized void startMonitoring() {
@@ -222,6 +307,10 @@
mIntentReceiver, intentFilter, null /* broadcastPermission */,
null /* scheduler */);
+ // Listen to EXTERNAL_APPLICATIONS_AVAILABLE is that an app becoming available means it may
+ // need to gain a permission. But an app that becomes unavailable can neither gain nor lose
+ // permissions on that account, it just can no longer run. Thus, doesn't need to listen to
+ // EXTERNAL_APPLICATIONS_UNAVAILABLE.
final IntentFilter externalIntentFilter =
new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
userAllContext.registerReceiver(
@@ -244,65 +333,18 @@
// mUidsAllowedOnRestrictedNetworks.
updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext));
- List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
- | MATCH_ANY_USER);
- if (apps == null) {
- loge("No apps");
- return;
+ final List<UserHandle> usrs = mUserManager.getUserHandles(true /* excludeDying */);
+ // Update netd permissions for all users.
+ for (UserHandle user : usrs) {
+ onUserAdded(user);
}
-
- final SparseIntArray netdPermsAppIds = new SparseIntArray();
-
- for (PackageInfo app : apps) {
- int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
- if (uid < 0) {
- continue;
- }
- final int appId = UserHandle.getAppId(uid);
- mAllApps.add(appId);
-
- final int permission = getPackageNetdNetworkPermission(app);
- if (isHigherNetworkPermission(permission, mApps.get(appId, PERMISSION_NONE))) {
- mApps.put(appId, permission);
- }
-
- //TODO: unify the management of the permissions into one codepath.
- int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
- app.requestedPermissionsFlags);
- netdPermsAppIds.put(appId, netdPermsAppIds.get(appId) | otherNetdPerms);
- }
-
- mUsers.addAll(mUserManager.getUserHandles(true /* excludeDying */));
-
- final SparseArray<String> netdPermToSystemPerm = new SparseArray<>();
- netdPermToSystemPerm.put(PERMISSION_INTERNET, INTERNET);
- netdPermToSystemPerm.put(PERMISSION_UPDATE_DEVICE_STATS, UPDATE_DEVICE_STATS);
- for (int i = 0; i < netdPermToSystemPerm.size(); i++) {
- final int netdPermission = netdPermToSystemPerm.keyAt(i);
- final String systemPermission = netdPermToSystemPerm.valueAt(i);
- final int[] hasPermissionUids =
- mSystemConfigManager.getSystemPermissionUids(systemPermission);
- for (int j = 0; j < hasPermissionUids.length; j++) {
- final int appId = UserHandle.getAppId(hasPermissionUids[j]);
- netdPermsAppIds.put(appId, netdPermsAppIds.get(appId) | netdPermission);
- }
- }
- log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
- update(mUsers, mApps, true);
- sendPackagePermissionsToNetd(netdPermsAppIds);
+ log("Users: " + mUsers.size() + ", UidToNetworkPerm: " + mUidToNetworkPerm.size());
}
@VisibleForTesting
synchronized void updateUidsAllowedOnRestrictedNetworks(final Set<Integer> uids) {
mUidsAllowedOnRestrictedNetworks.clear();
- // This is necessary for the app id to match in isUidAllowedOnRestrictedNetworks, and will
- // grant the permission to all uids associated with the app ID. This is safe even if the app
- // is only installed on some users because the uid cannot match some other app – this uid is
- // in effect not installed and can't be run.
- // TODO (b/192431153): Change appIds back to uids.
- for (int uid : uids) {
- mUidsAllowedOnRestrictedNetworks.add(UserHandle.getAppId(uid));
- }
+ mUidsAllowedOnRestrictedNetworks.addAll(uids);
}
@VisibleForTesting
@@ -316,7 +358,8 @@
return (appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo))
// Backward compatibility for b/114245686, on devices that launched before Q daemons
// and apps running as the system UID are exempted from this check.
- || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q);
+ || (UserHandle.getAppId(appInfo.uid) == SYSTEM_APPID
+ && mDeps.getDeviceFirstSdkInt() < VERSION_Q);
}
@VisibleForTesting
@@ -324,7 +367,7 @@
if (appInfo == null) return false;
// Check whether package's uid is in allowed on restricted networks uid list. If so, this
// uid can have netd system permission.
- return mUidsAllowedOnRestrictedNetworks.contains(UserHandle.getAppId(appInfo.uid));
+ return mUidsAllowedOnRestrictedNetworks.contains(appInfo.uid);
}
@VisibleForTesting
@@ -357,33 +400,30 @@
public synchronized boolean hasUseBackgroundNetworksPermission(final int uid) {
// Apps with any of the CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_INTERNAL or
// CONNECTIVITY_USE_RESTRICTED_NETWORKS permission has the permission to use background
- // networks. mApps contains the result of checks for both hasNetworkPermission and
- // hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
- // permissions at least.
- return mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE) != PERMISSION_NONE;
+ // networks. mUidToNetworkPerm contains the result of checks for hasNetworkPermission and
+ // hasRestrictedNetworkPermission, as well as the list of UIDs allowed on restricted
+ // networks. If uid is in the mUidToNetworkPerm list that means uid has one of permissions
+ // at least.
+ return mUidToNetworkPerm.get(uid, PERMISSION_NONE) != PERMISSION_NONE;
}
/**
* Returns whether the given uid has permission to use restricted networks.
*/
public synchronized boolean hasRestrictedNetworksPermission(int uid) {
- return PERMISSION_SYSTEM == mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE);
+ return PERMISSION_SYSTEM == mUidToNetworkPerm.get(uid, PERMISSION_NONE);
}
- private void update(Set<UserHandle> users, SparseIntArray apps, boolean add) {
+ private void sendUidsNetworkPermission(SparseIntArray uids, boolean add) {
List<Integer> network = new ArrayList<>();
List<Integer> system = new ArrayList<>();
- for (int i = 0; i < apps.size(); i++) {
- final int permission = apps.valueAt(i);
+ for (int i = 0; i < uids.size(); i++) {
+ final int permission = uids.valueAt(i);
if (PERMISSION_NONE == permission) {
continue; // Normally NONE is not stored in this map, but just in case
}
List<Integer> list = (PERMISSION_SYSTEM == permission) ? system : network;
- for (UserHandle user : users) {
- if (user == null) continue;
-
- list.add(user.getUid(apps.keyAt(i)));
- }
+ list.add(uids.keyAt(i));
}
try {
if (add) {
@@ -407,7 +447,19 @@
*/
public synchronized void onUserAdded(@NonNull UserHandle user) {
mUsers.add(user);
- update(Set.of(user), mApps, true);
+
+ final List<PackageInfo> apps = getInstalledPackagesAsUser(user);
+
+ // Save all apps
+ updateAllApps(apps);
+
+ // Uids network permissions
+ final SparseIntArray uids = makeUidsNetworkPerm(apps);
+ updateUidsNetworkPermission(uids);
+
+ // App ids traffic permission
+ final SparseIntArray appIds = makeAppIdsTrafficPerm(apps);
+ updateAppIdsTrafficPermission(appIds, getSystemTrafficPerm());
}
/**
@@ -419,44 +471,54 @@
*/
public synchronized void onUserRemoved(@NonNull UserHandle user) {
mUsers.remove(user);
- update(Set.of(user), mApps, false);
+
+ final SparseIntArray removedUids = new SparseIntArray();
+ final SparseIntArray allUids = mUidToNetworkPerm.clone();
+ for (int i = 0; i < allUids.size(); i++) {
+ final int uid = allUids.keyAt(i);
+ if (user.equals(UserHandle.getUserHandleForUid(uid))) {
+ mUidToNetworkPerm.delete(uid);
+ removedUids.put(uid, allUids.valueAt(i));
+ }
+ }
+ sendUidsNetworkPermission(removedUids, false /* add */);
}
/**
* Compare the current network permission and the given package's permission to find out highest
* permission for the uid.
*
+ * @param uid The target uid
* @param currentPermission Current uid network permission
* @param name The package has same uid that need compare its permission to update uid network
* permission.
*/
@VisibleForTesting
- protected int highestPermissionForUid(int currentPermission, String name) {
+ protected int highestPermissionForUid(int uid, int currentPermission, String name) {
+ // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
+ // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
if (currentPermission == PERMISSION_SYSTEM) {
return currentPermission;
}
- try {
- final PackageInfo app = mPackageManager.getPackageInfo(name,
- GET_PERMISSIONS | MATCH_ANY_USER);
- final int permission = getPackageNetdNetworkPermission(app);
- if (isHigherNetworkPermission(permission, currentPermission)) {
- return permission;
- }
- } catch (NameNotFoundException e) {
- // App not found.
- loge("NameNotFoundException " + name);
+ final PackageInfo app = getPackageInfoAsUser(name, UserHandle.getUserHandleForUid(uid));
+ if (app == null) return currentPermission;
+
+ final int permission = getPackageNetdNetworkPermission(app);
+ if (isHigherNetworkPermission(permission, currentPermission)) {
+ return permission;
}
return currentPermission;
}
- private int getPermissionForUid(final int uid) {
+ private int getTrafficPermissionForUid(final int uid) {
int permission = PERMISSION_NONE;
// Check all the packages for this UID. The UID has the permission if any of the
// packages in it has the permission.
final String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
for (String name : packages) {
- final PackageInfo app = getPackageInfo(name);
+ final PackageInfo app = getPackageInfoAsUser(name,
+ UserHandle.getUserHandleForUid(uid));
if (app != null && app.requestedPermissions != null) {
permission |= getNetdPermissionMask(app.requestedPermissions,
app.requestedPermissionsFlags);
@@ -481,6 +543,32 @@
}
/**
+ * This handles both network and traffic permission, because there is no overlap in actual
+ * values, where network permission is NETWORK or SYSTEM, and traffic permission is INTERNET
+ * or UPDATE_DEVICE_STATS
+ */
+ private String permissionToString(int permission) {
+ switch (permission) {
+ case PERMISSION_NONE:
+ return "NONE";
+ case PERMISSION_NETWORK:
+ return "NETWORK";
+ case PERMISSION_SYSTEM:
+ return "SYSTEM";
+ case PERMISSION_INTERNET:
+ return "INTERNET";
+ case PERMISSION_UPDATE_DEVICE_STATS:
+ return "UPDATE_DEVICE_STATS";
+ case (PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS):
+ return "ALL";
+ case PERMISSION_UNINSTALLED:
+ return "UNINSTALLED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ /**
* Called when a package is added.
*
* @param packageName The name of the new package.
@@ -490,25 +578,29 @@
*/
public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
final int appId = UserHandle.getAppId(uid);
- sendPackagePermissionsForAppId(appId, getPermissionForUid(uid));
+ final int trafficPerm = getTrafficPermissionForUid(uid);
+ sendPackagePermissionsForAppId(appId, trafficPerm);
- // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
- // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
- final int currentPermission = mApps.get(appId, PERMISSION_NONE);
- final int permission = highestPermissionForUid(currentPermission, packageName);
+ final int currentPermission = mUidToNetworkPerm.get(uid, PERMISSION_NONE);
+ final int permission = highestPermissionForUid(uid, currentPermission, packageName);
if (permission != currentPermission) {
- mApps.put(appId, permission);
+ mUidToNetworkPerm.put(uid, permission);
SparseIntArray apps = new SparseIntArray();
- apps.put(appId, permission);
- update(mUsers, apps, true);
+ apps.put(uid, permission);
+ sendUidsNetworkPermission(apps, true /* add */);
}
// If the newly-installed package falls within some VPN's uid range, update Netd with it.
- // This needs to happen after the mApps update above, since removeBypassingUids() in
- // updateVpnUid() depends on mApps to check if the package can bypass VPN.
+ // This needs to happen after the mUidToNetworkPerm update above, since
+ // removeBypassingUids() in updateVpnUid() depends on mUidToNetworkPerm to check if the
+ // package can bypass VPN.
updateVpnUid(uid, true /* add */);
mAllApps.add(appId);
+ mPermissionUpdateLogs.log("Package add: name=" + packageName + ", uid=" + uid
+ + ", nPerm=(" + permissionToString(permission) + "/"
+ + permissionToString(currentPermission) + ")"
+ + ", tPerm=" + permissionToString(trafficPerm));
}
private int highestUidNetworkPermission(int uid) {
@@ -518,7 +610,7 @@
for (String name : packages) {
// If multiple packages have the same UID, give the UID all permissions that
// any package in that UID has.
- permission = highestPermissionForUid(permission, name);
+ permission = highestPermissionForUid(uid, permission, name);
if (permission == PERMISSION_SYSTEM) {
break;
}
@@ -537,42 +629,42 @@
*/
public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
final int appId = UserHandle.getAppId(uid);
- sendPackagePermissionsForAppId(appId, getPermissionForUid(uid));
+ final int trafficPerm = getTrafficPermissionForUid(uid);
+ sendPackagePermissionsForAppId(appId, trafficPerm);
// If the newly-removed package falls within some VPN's uid range, update Netd with it.
- // This needs to happen before the mApps update below, since removeBypassingUids() in
- // updateVpnUid() depends on mApps to check if the package can bypass VPN.
+ // This needs to happen before the mUidToNetworkPerm update below, since
+ // removeBypassingUids() in updateVpnUid() depends on mUidToNetworkPerm to check if the
+ // package can bypass VPN.
updateVpnUid(uid, false /* add */);
// If the package has been removed from all users on the device, clear it form mAllApps.
if (mPackageManager.getNameForUid(uid) == null) {
mAllApps.remove(appId);
}
+ final int currentPermission = mUidToNetworkPerm.get(uid, PERMISSION_NONE);
final int permission = highestUidNetworkPermission(uid);
- if (permission == PERMISSION_SYSTEM) {
- // An app with this UID still has the SYSTEM permission.
- // Therefore, this UID must already have the SYSTEM permission.
- // Nothing to do.
- return;
- }
- // If the permissions of this UID have not changed, do nothing.
- if (permission == mApps.get(appId, PERMISSION_NONE)) return;
-
- final SparseIntArray apps = new SparseIntArray();
- if (permission != PERMISSION_NONE) {
- mApps.put(appId, permission);
- apps.put(appId, permission);
- update(mUsers, apps, true);
- } else {
- mApps.delete(appId);
- apps.put(appId, PERMISSION_NETWORK); // doesn't matter which permission we pick here
- update(mUsers, apps, false);
+ mPermissionUpdateLogs.log("Package remove: name=" + packageName + ", uid=" + uid
+ + ", nPerm=(" + permissionToString(permission) + "/"
+ + permissionToString(currentPermission) + ")"
+ + ", tPerm=" + permissionToString(trafficPerm));
+ if (permission != currentPermission) {
+ final SparseIntArray apps = new SparseIntArray();
+ if (permission == PERMISSION_NONE) {
+ mUidToNetworkPerm.delete(uid);
+ apps.put(uid, PERMISSION_NETWORK); // doesn't matter which permission we pick here
+ sendUidsNetworkPermission(apps, false);
+ } else {
+ mUidToNetworkPerm.put(uid, permission);
+ apps.put(uid, permission);
+ sendUidsNetworkPermission(apps, true);
+ }
}
}
private static int getNetdPermissionMask(String[] requestedPermissions,
int[] requestedPermissionsFlags) {
- int permissions = 0;
+ int permissions = PERMISSION_NONE;
if (requestedPermissions == null || requestedPermissionsFlags == null) return permissions;
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i].equals(INTERNET)
@@ -587,12 +679,23 @@
return permissions;
}
- private PackageInfo getPackageInfo(String packageName) {
+ private synchronized PackageManager getPackageManagerAsUser(UserHandle user) {
+ PackageManager pm = mUsersPackageManager.get(user);
+ if (pm == null) {
+ pm = mContext.createContextAsUser(user, 0 /* flag */).getPackageManager();
+ mUsersPackageManager.put(user, pm);
+ }
+ return pm;
+ }
+
+ private PackageInfo getPackageInfoAsUser(String packageName, UserHandle user) {
try {
- PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS
- | MATCH_ANY_USER);
- return app;
+ final PackageInfo info = getPackageManagerAsUser(user)
+ .getPackageInfo(packageName, GET_PERMISSIONS);
+ return info;
} catch (NameNotFoundException e) {
+ // App not found.
+ loge("NameNotFoundException " + packageName);
return null;
}
}
@@ -680,8 +783,7 @@
*/
private void removeBypassingUids(Set<Integer> uids, int vpnAppUid) {
uids.remove(vpnAppUid);
- uids.removeIf(uid ->
- mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE) == PERMISSION_SYSTEM);
+ uids.removeIf(uid -> mUidToNetworkPerm.get(uid, PERMISSION_NONE) == PERMISSION_SYSTEM);
}
/**
@@ -728,7 +830,7 @@
void sendPackagePermissionsForAppId(int appId, int permissions) {
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
netdPermissionsAppIds.put(appId, permissions);
- sendPackagePermissionsToNetd(netdPermissionsAppIds);
+ sendAppIdsTrafficPermission(netdPermissionsAppIds);
}
/**
@@ -740,16 +842,16 @@
* @hide
*/
@VisibleForTesting
- void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
+ void sendAppIdsTrafficPermission(SparseIntArray netdPermissionsAppIds) {
if (mNetd == null) {
Log.e(TAG, "Failed to get the netd service");
return;
}
- ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
- ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
- ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
- ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
- ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
+ final ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
+ final ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
+ final ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
+ final ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
+ final ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
int permissions = netdPermissionsAppIds.valueAt(i);
switch(permissions) {
@@ -820,20 +922,21 @@
for (Integer uid : uidsToUpdate) {
final int permission = highestUidNetworkPermission(uid);
- final int appId = UserHandle.getAppId(uid);
if (PERMISSION_NONE == permission) {
// Doesn't matter which permission is set here.
- removedUids.put(appId, PERMISSION_NETWORK);
- mApps.delete(appId);
+ removedUids.put(uid, PERMISSION_NETWORK);
+ mUidToNetworkPerm.delete(uid);
} else {
- updatedUids.put(appId, permission);
- mApps.put(appId, permission);
+ updatedUids.put(uid, permission);
+ mUidToNetworkPerm.put(uid, permission);
}
}
// Step3. Update or revoke permission for uids with netd.
- update(mUsers, updatedUids, true /* add */);
- update(mUsers, removedUids, false /* add */);
+ sendUidsNetworkPermission(updatedUids, true /* add */);
+ sendUidsNetworkPermission(removedUids, false /* add */);
+ mPermissionUpdateLogs.log("Setting change: update=" + updatedUids
+ + ", remove=" + removedUids);
}
private synchronized void onExternalApplicationsAvailable(String[] pkgList) {
@@ -843,11 +946,13 @@
}
for (String app : pkgList) {
- final PackageInfo info = getPackageInfo(app);
- if (info == null || info.applicationInfo == null) continue;
+ for (UserHandle user : mUsers) {
+ final PackageInfo info = getPackageInfoAsUser(app, user);
+ if (info == null || info.applicationInfo == null) continue;
- final int appId = info.applicationInfo.uid;
- onPackageAdded(app, appId); // Use onPackageAdded to add package one by one.
+ final int uid = info.applicationInfo.uid;
+ onPackageAdded(app, uid); // Use onPackageAdded to add package one by one.
+ }
}
}
@@ -861,6 +966,12 @@
pw.println();
}
pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Update logs:");
+ pw.increaseIndent();
+ mPermissionUpdateLogs.reverseDump(pw);
+ pw.decreaseIndent();
}
private static void log(String s) {
diff --git a/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
similarity index 80%
rename from service/src/com/android/server/connectivity/ProfileNetworkPreferences.java
rename to service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
index dd2815d..71f342d 100644
--- a/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java
+++ b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
@@ -30,7 +30,7 @@
*
* A given profile can only have one preference.
*/
-public class ProfileNetworkPreferences {
+public class ProfileNetworkPreferenceList {
/**
* A single preference, as it applies to a given user profile.
*/
@@ -38,26 +38,32 @@
@NonNull public final UserHandle user;
// Capabilities are only null when sending an object to remove the setting for a user
@Nullable public final NetworkCapabilities capabilities;
+ public final boolean allowFallback;
public Preference(@NonNull final UserHandle user,
- @Nullable final NetworkCapabilities capabilities) {
+ @Nullable final NetworkCapabilities capabilities,
+ final boolean allowFallback) {
this.user = user;
this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
+ this.allowFallback = allowFallback;
}
/** toString */
public String toString() {
- return "[ProfileNetworkPreference user=" + user + " caps=" + capabilities + "]";
+ return "[ProfileNetworkPreference user=" + user
+ + " caps=" + capabilities
+ + " allowFallback=" + allowFallback
+ + "]";
}
}
@NonNull public final List<Preference> preferences;
- public ProfileNetworkPreferences() {
+ public ProfileNetworkPreferenceList() {
preferences = Collections.EMPTY_LIST;
}
- private ProfileNetworkPreferences(@NonNull final List<Preference> list) {
+ private ProfileNetworkPreferenceList(@NonNull final List<Preference> list) {
preferences = Collections.unmodifiableList(list);
}
@@ -68,7 +74,7 @@
* preference. Passing a Preference object containing a null capabilities object is equivalent
* to (and indeed, implemented as) removing the preference for this user.
*/
- public ProfileNetworkPreferences plus(@NonNull final Preference pref) {
+ public ProfileNetworkPreferenceList plus(@NonNull final Preference pref) {
final ArrayList<Preference> newPrefs = new ArrayList<>();
for (final Preference existingPref : preferences) {
if (!existingPref.user.equals(pref.user)) {
@@ -78,7 +84,7 @@
if (null != pref.capabilities) {
newPrefs.add(pref);
}
- return new ProfileNetworkPreferences(newPrefs);
+ return new ProfileNetworkPreferenceList(newPrefs);
}
public boolean isEmpty() {
diff --git a/service/src/com/android/server/connectivity/ProxyTracker.java b/service/src/com/android/server/connectivity/ProxyTracker.java
index f572b46..bc0929c 100644
--- a/service/src/com/android/server/connectivity/ProxyTracker.java
+++ b/service/src/com/android/server/connectivity/ProxyTracker.java
@@ -55,6 +55,11 @@
private static final String TAG = ProxyTracker.class.getSimpleName();
private static final boolean DBG = true;
+ // EXTRA_PROXY_INFO is now @removed. In order to continue sending it, hardcode its value here.
+ // The Proxy.EXTRA_PROXY_INFO constant is not visible to this code because android.net.Proxy
+ // a hidden platform constant not visible to mainline modules.
+ private static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
+
@NonNull
private final Context mContext;
@@ -253,7 +258,7 @@
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxyInfo);
+ intent.putExtra(EXTRA_PROXY_INFO, proxyInfo);
final long ident = Binder.clearCallingIdentity();
try {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
diff --git a/service/src/com/android/server/connectivity/TcpKeepaliveController.java b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
index c480594..acfbb3c 100644
--- a/service/src/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
import static android.net.SocketKeepalive.DATA_RECEIVED;
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE;
import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
@@ -29,6 +30,8 @@
import static android.system.OsConstants.IP_TTL;
import static android.system.OsConstants.TIOCOUTQ;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
+
import android.annotation.NonNull;
import android.net.InvalidPacketException;
import android.net.NetworkUtils;
@@ -36,7 +39,6 @@
import android.net.TcpKeepalivePacketData;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.TcpRepairWindow;
-import android.net.util.KeepalivePacketDataUtil;
import android.os.Handler;
import android.os.MessageQueue;
import android.os.Messenger;
@@ -46,12 +48,18 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.IpUtils;
import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
import java.io.FileDescriptor;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* Manage tcp socket which offloads tcp keepalive.
@@ -82,6 +90,8 @@
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+ private static final int TCP_HEADER_LENGTH = 20;
+
// Reference include/uapi/linux/tcp.h
private static final int TCP_REPAIR = 19;
private static final int TCP_REPAIR_QUEUE = 20;
@@ -112,12 +122,81 @@
throws InvalidPacketException, InvalidSocketException {
try {
final TcpKeepalivePacketDataParcelable tcpDetails = switchToRepairMode(fd);
- return KeepalivePacketDataUtil.fromStableParcelable(tcpDetails);
+ // TODO: consider building a TcpKeepalivePacketData directly from switchToRepairMode
+ return fromStableParcelable(tcpDetails);
} catch (InvalidPacketException | InvalidSocketException e) {
switchOutOfRepairMode(fd);
throw e;
}
}
+
+ /**
+ * Factory method to create tcp keepalive packet structure.
+ */
+ @VisibleForTesting
+ public static TcpKeepalivePacketData fromStableParcelable(
+ TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
+ final byte[] packet;
+ try {
+ if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
+ && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
+ && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
+ packet = buildV4Packet(tcpDetails);
+ } else {
+ // TODO: support ipv6
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+ return new TcpKeepalivePacketData(
+ InetAddress.getByAddress(tcpDetails.srcAddress),
+ tcpDetails.srcPort,
+ InetAddress.getByAddress(tcpDetails.dstAddress),
+ tcpDetails.dstPort,
+ packet,
+ tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale,
+ tcpDetails.tos, tcpDetails.ttl);
+ } catch (UnknownHostException e) {
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+ }
+
+ /**
+ * Build ipv4 tcp keepalive packet, not including the link-layer header.
+ */
+ // TODO : if this code is ever moved to the network stack, factorize constants with the ones
+ // over there.
+ // TODO: consider using Ipv4Utils.buildTcpv4Packet() instead
+ private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
+ final int length = IPV4_HEADER_MIN_LEN + TCP_HEADER_LENGTH;
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.put((byte) 0x45); // IP version and IHL
+ buf.put((byte) tcpDetails.tos); // TOS
+ buf.putShort((short) length);
+ buf.putInt(0x00004000); // ID, flags=DF, offset
+ buf.put((byte) tcpDetails.ttl); // TTL
+ buf.put((byte) IPPROTO_TCP);
+ final int ipChecksumOffset = buf.position();
+ buf.putShort((short) 0); // IP checksum
+ buf.put(tcpDetails.srcAddress);
+ buf.put(tcpDetails.dstAddress);
+ buf.putShort((short) tcpDetails.srcPort);
+ buf.putShort((short) tcpDetails.dstPort);
+ buf.putInt(tcpDetails.seq); // Sequence Number
+ buf.putInt(tcpDetails.ack); // ACK
+ buf.putShort((short) 0x5010); // TCP length=5, flags=ACK
+ buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size
+ final int tcpChecksumOffset = buf.position();
+ buf.putShort((short) 0); // TCP checksum
+ // URG is not set therefore the urgent pointer is zero.
+ buf.putShort((short) 0); // Urgent pointer
+
+ buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0));
+ buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
+ buf, 0, IPV4_HEADER_MIN_LEN, TCP_HEADER_LENGTH));
+
+ return buf.array();
+ }
+
/**
* Switch the tcp socket to repair mode and query detail tcp information.
*
diff --git a/service/src/com/android/server/connectivity/UidRangeUtils.java b/service/src/com/android/server/connectivity/UidRangeUtils.java
new file mode 100644
index 0000000..7318296
--- /dev/null
+++ b/service/src/com/android/server/connectivity/UidRangeUtils.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.net.UidRange;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Utility class for UidRange
+ *
+ * @hide
+ */
+public final class UidRangeUtils {
+ /**
+ * Check if given uid range set is within the uid range
+ * @param uids uid range in which uidRangeSet is checked to be in range.
+ * @param uidRangeSet uid range set to be be checked if it is in range of uids
+ * @return true uidRangeSet is in the range of uids
+ * @hide
+ */
+ public static boolean isRangeSetInUidRange(@NonNull UidRange uids,
+ @NonNull Set<UidRange> uidRangeSet) {
+ Objects.requireNonNull(uids);
+ Objects.requireNonNull(uidRangeSet);
+ if (uidRangeSet.size() == 0) {
+ return true;
+ }
+ for (UidRange range : uidRangeSet) {
+ if (!uids.contains(range.start) || !uids.contains(range.stop)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Remove given uid ranges set from a uid range
+ * @param uids uid range from which uidRangeSet will be removed
+ * @param uidRangeSet uid range set to be removed from uids.
+ * WARNING : This function requires the UidRanges in uidRangeSet to be disjoint
+ * WARNING : This function requires the arrayset to be iterated in increasing order of the
+ * ranges. Today this is provided by the iteration order stability of
+ * ArraySet, and the fact that the code creating this ArraySet always
+ * creates it in increasing order.
+ * Note : if any of the above is not satisfied this function throws IllegalArgumentException
+ * TODO : remove these limitations
+ * @hide
+ */
+ public static ArraySet<UidRange> removeRangeSetFromUidRange(@NonNull UidRange uids,
+ @NonNull ArraySet<UidRange> uidRangeSet) {
+ Objects.requireNonNull(uids);
+ Objects.requireNonNull(uidRangeSet);
+ final ArraySet<UidRange> filteredRangeSet = new ArraySet<UidRange>();
+ if (uidRangeSet.size() == 0) {
+ filteredRangeSet.add(uids);
+ return filteredRangeSet;
+ }
+
+ int start = uids.start;
+ UidRange previousRange = null;
+ for (UidRange uidRange : uidRangeSet) {
+ if (previousRange != null) {
+ if (previousRange.stop > uidRange.start) {
+ throw new IllegalArgumentException("UID ranges are not increasing order");
+ }
+ }
+ if (uidRange.start > start) {
+ filteredRangeSet.add(new UidRange(start, uidRange.start - 1));
+ start = uidRange.stop + 1;
+ } else if (uidRange.start == start) {
+ start = uidRange.stop + 1;
+ }
+ previousRange = uidRange;
+ }
+ if (start < uids.stop) {
+ filteredRangeSet.add(new UidRange(start, uids.stop));
+ }
+ return filteredRangeSet;
+ }
+
+ /**
+ * Compare if the given UID range sets have overlapping uids
+ * @param uidRangeSet1 first uid range set to check for overlap
+ * @param uidRangeSet2 second uid range set to check for overlap
+ * @hide
+ */
+ public static boolean doesRangeSetOverlap(@NonNull Set<UidRange> uidRangeSet1,
+ @NonNull Set<UidRange> uidRangeSet2) {
+ Objects.requireNonNull(uidRangeSet1);
+ Objects.requireNonNull(uidRangeSet2);
+
+ if (uidRangeSet1.size() == 0 || uidRangeSet2.size() == 0) {
+ return false;
+ }
+ for (UidRange range1 : uidRangeSet1) {
+ for (UidRange range2 : uidRangeSet2) {
+ if (range1.contains(range2.start) || range1.contains(range2.stop)
+ || range2.contains(range1.start) || range2.contains(range1.stop)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Convert a list of uid to set of UidRanges.
+ * @param uids list of uids
+ * @return set of UidRanges
+ * @hide
+ */
+ public static ArraySet<UidRange> convertListToUidRange(@NonNull List<Integer> uids) {
+ Objects.requireNonNull(uids);
+ final ArraySet<UidRange> uidRangeSet = new ArraySet<UidRange>();
+ if (uids.size() == 0) {
+ return uidRangeSet;
+ }
+ List<Integer> uidsNew = new ArrayList<>(uids);
+ Collections.sort(uidsNew);
+ int start = uidsNew.get(0);
+ int stop = start;
+
+ for (Integer i : uidsNew) {
+ if (i <= stop + 1) {
+ stop = i;
+ } else {
+ uidRangeSet.add(new UidRange(start, stop));
+ start = i;
+ stop = i;
+ }
+ }
+ uidRangeSet.add(new UidRange(start, stop));
+ return uidRangeSet;
+ }
+}
diff --git a/tests/common/Android.bp b/tests/common/Android.bp
index e1fab09..acf04bf 100644
--- a/tests/common/Android.bp
+++ b/tests/common/Android.bp
@@ -64,9 +64,8 @@
name: "ConnectivityCoverageTests",
// Tethering started on SDK 30
min_sdk_version: "30",
- // TODO: change to 31 as soon as it is available
- target_sdk_version: "30",
- test_suites: ["general-tests", "mts"],
+ target_sdk_version: "31",
+ test_suites: ["general-tests", "mts-tethering"],
defaults: [
"framework-connectivity-test-defaults",
"FrameworksNetTests-jni-defaults",
@@ -89,7 +88,7 @@
"libstaticjvmtiagent",
// For NetworkStackUtils included in NetworkStackBase
"libnetworkstackutilsjni",
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
// For framework tests
"libservice-connectivity",
],
@@ -115,9 +114,33 @@
// meaning @hide APIs in framework-connectivity are resolved before @SystemApi
// stubs in framework
"framework-connectivity.impl",
+ "framework-connectivity-tiramisu.impl",
+ "framework-tethering.impl",
"framework",
// if sdk_version="" this gets automatically included, but here we need to add manually.
"framework-res",
],
}
+
+// Defaults for tests that want to run in mainline-presubmit.
+// Not widely used because many of our tests have AndroidTest.xml files and
+// use the mainline-param config-descriptor metadata in AndroidTest.xml.
+
+// test_mainline_modules is an array of strings. Each element in the array is a list of modules
+// separated by "+". The modules in this list must be in alphabetical order.
+// See SuiteModuleLoader.java.
+// TODO: why are the modules separated by + instead of being separate entries in the array?
+mainline_presubmit_modules = [
+ "CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex",
+]
+
+cc_defaults {
+ name: "connectivity-mainline-presubmit-cc-defaults",
+ test_mainline_modules: mainline_presubmit_modules,
+}
+
+java_defaults {
+ name: "connectivity-mainline-presubmit-java-defaults",
+ test_mainline_modules: mainline_presubmit_modules,
+}
diff --git a/tests/common/java/ParseExceptionTest.kt b/tests/common/java/ParseExceptionTest.kt
index b702d61..ca01c76 100644
--- a/tests/common/java/ParseExceptionTest.kt
+++ b/tests/common/java/ParseExceptionTest.kt
@@ -18,6 +18,7 @@
import android.os.Build
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNull
@@ -27,6 +28,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@ConnectivityModuleTest
class ParseExceptionTest {
@get:Rule
val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.R)
diff --git a/tests/common/java/android/net/CaptivePortalDataTest.kt b/tests/common/java/android/net/CaptivePortalDataTest.kt
index 18a9331..f927380 100644
--- a/tests/common/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/common/java/android/net/CaptivePortalDataTest.kt
@@ -19,7 +19,6 @@
import android.os.Build
import androidx.test.filters.SmallTest
import com.android.modules.utils.build.SdkLevel
-import com.android.testutils.assertParcelSane
import com.android.testutils.assertParcelingIsLossless
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -71,9 +70,8 @@
@Test
fun testParcelUnparcel() {
- val fieldCount = if (SdkLevel.isAtLeastS()) 10 else 7
- assertParcelSane(data, fieldCount)
- assertParcelSane(dataFromPasspoint, fieldCount)
+ assertParcelingIsLossless(data)
+ assertParcelingIsLossless(dataFromPasspoint)
assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
diff --git a/tests/common/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/common/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 294ed10..03a9a80 100644
--- a/tests/common/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/common/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -21,7 +21,7 @@
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
-import static com.android.testutils.ParcelUtils.assertParcelSane;
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -202,7 +202,7 @@
@Test
public void testConnectivityReportParcelUnparcel() {
- assertParcelSane(createSampleConnectivityReport(), 5);
+ assertParcelingIsLossless(createSampleConnectivityReport());
}
private DataStallReport createSampleDataStallReport() {
@@ -303,7 +303,7 @@
@Test
public void testDataStallReportParcelUnparcel() {
- assertParcelSane(createSampleDataStallReport(), 6);
+ assertParcelingIsLossless(createSampleDataStallReport());
}
@Test
diff --git a/tests/common/java/android/net/DhcpInfoTest.java b/tests/common/java/android/net/DhcpInfoTest.java
index ab4726b..b42e183 100644
--- a/tests/common/java/android/net/DhcpInfoTest.java
+++ b/tests/common/java/android/net/DhcpInfoTest.java
@@ -17,7 +17,6 @@
package android.net;
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTL;
-import static com.android.testutils.MiscAsserts.assertFieldCountEquals;
import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
import static org.junit.Assert.assertEquals;
@@ -101,7 +100,6 @@
// Cannot use assertParcelSane() here because this requires .equals() to work as
// defined, but DhcpInfo has a different legacy behavior that we cannot change.
final DhcpInfo dhcpInfo = createDhcpInfoObject();
- assertFieldCountEquals(7, DhcpInfo.class);
final DhcpInfo dhcpInfoRoundTrip = parcelingRoundTrip(dhcpInfo);
assertTrue(dhcpInfoEquals(null, null));
diff --git a/tests/common/java/android/net/IpPrefixTest.java b/tests/common/java/android/net/IpPrefixTest.java
index 50ecb42..fef6416 100644
--- a/tests/common/java/android/net/IpPrefixTest.java
+++ b/tests/common/java/android/net/IpPrefixTest.java
@@ -17,7 +17,6 @@
package android.net;
import static com.android.testutils.MiscAsserts.assertEqualBothWays;
-import static com.android.testutils.MiscAsserts.assertFieldCountEquals;
import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
@@ -31,6 +30,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.ConnectivityModuleTest;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,6 +40,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class IpPrefixTest {
private static InetAddress address(String addr) {
@@ -122,6 +124,9 @@
p = new IpPrefix("[2001:db8::123]/64");
assertEquals("2001:db8::/64", p.toString());
+
+ p = new IpPrefix(InetAddresses.parseNumericAddress("::128"), 64);
+ assertEquals("::/64", p.toString());
}
@Test
@@ -368,7 +373,5 @@
p = new IpPrefix("192.0.2.0/25");
assertParcelingIsLossless(p);
assertTrue(p.isIPv4());
-
- assertFieldCountEquals(2, IpPrefix.class);
}
}
diff --git a/tests/common/java/android/net/LinkAddressTest.java b/tests/common/java/android/net/LinkAddressTest.java
index 2cf3cf9..6b04fee 100644
--- a/tests/common/java/android/net/LinkAddressTest.java
+++ b/tests/common/java/android/net/LinkAddressTest.java
@@ -28,7 +28,6 @@
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import static com.android.testutils.MiscAsserts.assertEqualBothWays;
-import static com.android.testutils.MiscAsserts.assertFieldCountEquals;
import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
@@ -44,8 +43,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.Rule;
@@ -63,6 +62,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class LinkAddressTest {
@Rule
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
@@ -352,17 +352,6 @@
assertParcelingIsLossless(l);
}
- @Test @IgnoreAfter(Build.VERSION_CODES.Q)
- public void testFieldCount_Q() {
- assertFieldCountEquals(4, LinkAddress.class);
- }
-
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
- public void testFieldCount() {
- // Make sure any new field is covered by the above parceling tests when changing this number
- assertFieldCountEquals(6, LinkAddress.class);
- }
-
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testDeprecationTime() {
try {
diff --git a/tests/common/java/android/net/LinkPropertiesTest.java b/tests/common/java/android/net/LinkPropertiesTest.java
index 550953d..4d85a57 100644
--- a/tests/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/common/java/android/net/LinkPropertiesTest.java
@@ -20,7 +20,6 @@
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.RouteInfo.RTN_UNREACHABLE;
-import static com.android.testutils.ParcelUtils.assertParcelSane;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
@@ -41,6 +40,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -60,6 +60,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class LinkPropertiesTest {
@Rule
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
@@ -1006,7 +1007,7 @@
@Test @IgnoreAfter(Build.VERSION_CODES.Q)
public void testLinkPropertiesParcelable_Q() throws Exception {
final LinkProperties source = makeLinkPropertiesForParceling();
- assertParcelSane(source, 14 /* fieldCount */);
+ assertParcelingIsLossless(source);
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -1017,8 +1018,7 @@
source.setCaptivePortalApiUrl(CAPPORT_API_URL);
source.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
source.setDhcpServerAddress((Inet4Address) GATEWAY1);
- assertParcelSane(new LinkProperties(source, true /* parcelSensitiveFields */),
- 18 /* fieldCount */);
+ assertParcelingIsLossless(new LinkProperties(source, true /* parcelSensitiveFields */));
// Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared.
final LinkProperties sanitized = new LinkProperties(source);
diff --git a/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt
index a5e44d5..4a4859d 100644
--- a/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt
+++ b/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt
@@ -22,14 +22,11 @@
import android.os.Build
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-
-import com.android.testutils.assertParcelSane
+import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
-
-import java.lang.IllegalStateException
-
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertFalse
import org.junit.Rule
import org.junit.Test
@@ -38,6 +35,7 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
+@ConnectivityModuleTest
class MatchAllNetworkSpecifierTest {
@Rule @JvmField
val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
@@ -50,7 +48,7 @@
@Test
fun testParcel() {
- assertParcelSane(MatchAllNetworkSpecifier(), 0)
+ assertParcelingIsLossless(MatchAllNetworkSpecifier())
}
@Test
diff --git a/tests/common/java/android/net/NattKeepalivePacketDataTest.kt b/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
index 46f39dd..ad7a526 100644
--- a/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
+++ b/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
@@ -23,10 +23,9 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.testutils.assertEqualBothWays
-import com.android.testutils.assertFieldCountEquals
-import com.android.testutils.assertParcelSane
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.assertParcelingIsLossless
import com.android.testutils.parcelingRoundTrip
import java.net.InetAddress
import org.junit.Assert.assertEquals
@@ -93,7 +92,7 @@
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testParcel() {
- assertParcelSane(nattKeepalivePacket(), 0)
+ assertParcelingIsLossless(nattKeepalivePacket())
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -103,8 +102,6 @@
assertNotEquals(nattKeepalivePacket(srcAddress = TEST_DST_ADDRV4), nattKeepalivePacket())
// Test src port only because dst port have to be NATT_PORT
assertNotEquals(nattKeepalivePacket(srcPort = TEST_PORT2), nattKeepalivePacket())
- // Make sure the parceling test is updated if fields are added in the base class.
- assertFieldCountEquals(5, KeepalivePacketData::class.java)
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/common/java/android/net/NetworkAgentConfigTest.kt b/tests/common/java/android/net/NetworkAgentConfigTest.kt
index afaae1c..b339a27 100644
--- a/tests/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/common/java/android/net/NetworkAgentConfigTest.kt
@@ -20,9 +20,10 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.modules.utils.build.SdkLevel.isAtLeastS
+import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -32,6 +33,7 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
+@ConnectivityModuleTest
class NetworkAgentConfigTest {
@Rule @JvmField
val ignoreRule = DevSdkIgnoreRule()
@@ -48,13 +50,7 @@
setBypassableVpn(true)
}
}.build()
- if (isAtLeastS()) {
- // From S, the config will have 12 items
- assertParcelSane(config, 12)
- } else {
- // For R or below, the config will have 10 items
- assertParcelSane(config, 10)
- }
+ assertParcelingIsLossless(config)
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 502b4f6..09d36e5 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -22,6 +22,12 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
@@ -50,9 +56,10 @@
import static com.android.modules.utils.build.SdkLevel.isAtLeastR;
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static com.android.testutils.MiscAsserts.assertEmpty;
import static com.android.testutils.MiscAsserts.assertThrows;
-import static com.android.testutils.ParcelUtils.assertParcelSane;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static org.junit.Assert.assertArrayEquals;
@@ -84,11 +91,14 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class NetworkCapabilitiesTest {
private static final String TEST_SSID = "TEST_SSID";
private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
@@ -281,19 +291,11 @@
assertFalse(netCap2.satisfiedByUids(netCap));
assertFalse(netCap.appliesToUid(650));
assertTrue(netCap2.appliesToUid(650));
- netCap.combineCapabilities(netCap2);
+ netCap.setUids(uids);
assertTrue(netCap2.satisfiedByUids(netCap));
assertTrue(netCap.appliesToUid(650));
assertFalse(netCap.appliesToUid(500));
- assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
- netCap.combineCapabilities(new NetworkCapabilities());
- assertTrue(netCap.appliesToUid(500));
- assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000)));
- assertFalse(netCap2.appliesToUid(500));
- assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000)));
- assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
-
// Null uids satisfies everything.
netCap.setUids(null);
assertTrue(netCap2.satisfiedByUids(netCap));
@@ -342,13 +344,7 @@
}
private void testParcelSane(NetworkCapabilities cap) {
- if (isAtLeastS()) {
- assertParcelSane(cap, 16);
- } else if (isAtLeastR()) {
- assertParcelSane(cap, 15);
- } else {
- assertParcelSane(cap, 11);
- }
+ assertParcelingIsLossless(cap);
}
private static NetworkCapabilities createNetworkCapabilitiesWithTransportInfo() {
@@ -578,103 +574,6 @@
}
@Test
- public void testCombineCapabilities() {
- NetworkCapabilities nc1 = new NetworkCapabilities();
- NetworkCapabilities nc2 = new NetworkCapabilities();
-
- if (isAtLeastS()) {
- nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
- }
- nc1.addCapability(NET_CAPABILITY_NOT_ROAMING);
- assertNotEquals(nc1, nc2);
- nc2.combineCapabilities(nc1);
- assertEquals(nc1, nc2);
- assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
- if (isAtLeastS()) {
- assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
- }
-
- if (isAtLeastS()) {
- // This will effectively move NOT_ROAMING capability from required to forbidden for nc1.
- nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
- // It is not allowed to have the same capability in both wanted and forbidden list.
- assertThrows(IllegalArgumentException.class, () -> nc2.combineCapabilities(nc1));
- // Remove forbidden capability to continue other tests.
- nc1.removeForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
- }
-
- nc1.setSSID(TEST_SSID);
- nc2.combineCapabilities(nc1);
- if (isAtLeastR()) {
- assertTrue(TEST_SSID.equals(nc2.getSsid()));
- }
-
- // Because they now have the same SSID, the following call should not throw
- nc2.combineCapabilities(nc1);
-
- nc1.setSSID(DIFFERENT_TEST_SSID);
- try {
- nc2.combineCapabilities(nc1);
- fail("Expected IllegalStateException: can't combine different SSIDs");
- } catch (IllegalStateException expected) {}
- nc1.setSSID(TEST_SSID);
-
- if (isAtLeastS()) {
- nc1.setUids(uidRanges(10, 13));
- assertNotEquals(nc1, nc2);
- nc2.combineCapabilities(nc1); // Everything + 10~13 is still everything.
- assertNotEquals(nc1, nc2);
- nc1.combineCapabilities(nc2); // 10~13 + everything is everything.
- assertEquals(nc1, nc2);
- nc1.setUids(uidRanges(10, 13));
- nc2.setUids(uidRanges(20, 23));
- assertNotEquals(nc1, nc2);
- nc1.combineCapabilities(nc2);
- assertTrue(nc1.appliesToUid(12));
- assertFalse(nc2.appliesToUid(12));
- assertTrue(nc1.appliesToUid(22));
- assertTrue(nc2.appliesToUid(22));
-
- // Verify the subscription id list can be combined only when they are equal.
- nc1.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
- nc2.setSubscriptionIds(Set.of(TEST_SUBID2));
- assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
-
- nc2.setSubscriptionIds(Set.of());
- assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
-
- nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1));
- nc2.combineCapabilities(nc1);
- assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubscriptionIds());
- }
- }
-
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
- public void testCombineCapabilities_AdministratorUids() {
- final NetworkCapabilities nc1 = new NetworkCapabilities();
- final NetworkCapabilities nc2 = new NetworkCapabilities();
-
- final int[] adminUids = {3, 6, 12};
- nc1.setAdministratorUids(adminUids);
- nc2.combineCapabilities(nc1);
- assertTrue(nc2.equalsAdministratorUids(nc1));
- assertArrayEquals(nc2.getAdministratorUids(), adminUids);
-
- final int[] adminUidsOtherOrder = {3, 12, 6};
- nc1.setAdministratorUids(adminUidsOtherOrder);
- assertTrue(nc2.equalsAdministratorUids(nc1));
-
- final int[] adminUids2 = {11, 1, 12, 3, 6};
- nc1.setAdministratorUids(adminUids2);
- assertFalse(nc2.equalsAdministratorUids(nc1));
- assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2));
- try {
- nc2.combineCapabilities(nc1);
- fail("Shouldn't be able to combine different lists of admin UIDs");
- } catch (IllegalStateException expected) { }
- }
-
- @Test
public void testSetCapabilities() {
final int[] REQUIRED_CAPABILITIES = new int[] {
NET_CAPABILITY_INTERNET, NET_CAPABILITY_NOT_VPN };
@@ -712,6 +611,47 @@
}
@Test
+ public void testUnderlyingNetworks() {
+ assumeTrue(isAtLeastT());
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ final Network network1 = new Network(100);
+ final Network network2 = new Network(101);
+ final ArrayList<Network> inputNetworks = new ArrayList<>();
+ inputNetworks.add(network1);
+ inputNetworks.add(network2);
+ nc.setUnderlyingNetworks(inputNetworks);
+ final ArrayList<Network> outputNetworks = new ArrayList<>(nc.getUnderlyingNetworks());
+ assertEquals(network1, outputNetworks.get(0));
+ assertEquals(network2, outputNetworks.get(1));
+ nc.setUnderlyingNetworks(null);
+ assertNull(nc.getUnderlyingNetworks());
+ }
+
+ @Test
+ public void testEqualsForUnderlyingNetworks() {
+ assumeTrue(isAtLeastT());
+ final NetworkCapabilities nc1 = new NetworkCapabilities();
+ final NetworkCapabilities nc2 = new NetworkCapabilities();
+ assertEquals(nc1, nc2);
+ final Network network = new Network(100);
+ final ArrayList<Network> inputNetworks = new ArrayList<>();
+ final ArrayList<Network> emptyList = new ArrayList<>();
+ inputNetworks.add(network);
+ nc1.setUnderlyingNetworks(inputNetworks);
+ assertNotEquals(nc1, nc2);
+ nc2.setUnderlyingNetworks(inputNetworks);
+ assertEquals(nc1, nc2);
+ nc1.setUnderlyingNetworks(emptyList);
+ assertNotEquals(nc1, nc2);
+ nc2.setUnderlyingNetworks(emptyList);
+ assertEquals(nc1, nc2);
+ nc1.setUnderlyingNetworks(null);
+ assertNotEquals(nc1, nc2);
+ nc2.setUnderlyingNetworks(null);
+ assertEquals(nc1, nc2);
+ }
+
+ @Test
public void testSetNetworkSpecifierOnMultiTransportNc() {
// Sequence 1: Transport + Transport + NetworkSpecifier
NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -749,29 +689,6 @@
}
@Test
- public void testCombineTransportInfo() {
- NetworkCapabilities nc1 = new NetworkCapabilities();
- nc1.setTransportInfo(new TestTransportInfo());
-
- NetworkCapabilities nc2 = new NetworkCapabilities();
- // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
- // combine fails)
- nc2.setTransportInfo(new TestTransportInfo());
-
- try {
- nc1.combineCapabilities(nc2);
- fail("Should not be able to combine NetworkCabilities which contain TransportInfos");
- } catch (IllegalStateException expected) {
- // empty
- }
-
- // verify that can combine with identical TransportInfo objects
- NetworkCapabilities nc3 = new NetworkCapabilities();
- nc3.setTransportInfo(nc1.getTransportInfo());
- nc1.combineCapabilities(nc3);
- }
-
- @Test
public void testSet() {
NetworkCapabilities nc1 = new NetworkCapabilities();
NetworkCapabilities nc2 = new NetworkCapabilities();
@@ -872,6 +789,87 @@
} catch (IllegalStateException expected) { }
}
+ @Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+ public void testEnterpriseCapabilitySubLevel() {
+ final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_ENTERPRISE)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .build();
+ assertEquals(1, nc1.getEnterpriseCapabilitySubLevels().length);
+ assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
+ nc1.getEnterpriseCapabilitySubLevels()[0]);
+ final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_ENTERPRISE)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+ .build();
+ assertEquals(2, nc2.getEnterpriseCapabilitySubLevels().length);
+ assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
+ nc2.getEnterpriseCapabilitySubLevels()[0]);
+ assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2,
+ nc2.getEnterpriseCapabilitySubLevels()[1]);
+ final NetworkCapabilities nc3 = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_ENTERPRISE)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5)
+ .build();
+ assertEquals(5, nc3.getEnterpriseCapabilitySubLevels().length);
+ assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
+ nc3.getEnterpriseCapabilitySubLevels()[0]);
+ assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2,
+ nc3.getEnterpriseCapabilitySubLevels()[1]);
+ assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3,
+ nc3.getEnterpriseCapabilitySubLevels()[2]);
+ assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4,
+ nc3.getEnterpriseCapabilitySubLevels()[3]);
+ assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5,
+ nc3.getEnterpriseCapabilitySubLevels()[4]);
+
+ final Class<IllegalArgumentException> illegalArgumentExceptionClass =
+ IllegalArgumentException.class;
+ assertThrows(illegalArgumentExceptionClass, () -> new NetworkCapabilities.Builder()
+ .addEnterpriseCapabilitySubLevel(6)
+ .build());
+ assertThrows(illegalArgumentExceptionClass, () -> new NetworkCapabilities.Builder()
+ .removeEnterpriseCapabilitySubLevel(6)
+ .build());
+
+ final Class<IllegalStateException> illegalStateException =
+ IllegalStateException.class;
+ assertThrows(illegalStateException, () -> new NetworkCapabilities.Builder()
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .build());
+
+ final NetworkCapabilities nc4 = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_ENTERPRISE)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+ .removeEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .removeEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+ .build();
+ assertEquals(0, nc4.getEnterpriseCapabilitySubLevels().length);
+
+ final NetworkCapabilities nc5 = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_CBS)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+ .removeEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .removeEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+ .build();
+
+ assertTrue(nc4.satisfiedByNetworkCapabilities(nc1));
+ assertFalse(nc1.satisfiedByNetworkCapabilities(nc4));
+
+ assertFalse(nc3.satisfiedByNetworkCapabilities(nc2));
+ assertTrue(nc2.satisfiedByNetworkCapabilities(nc3));
+
+ assertFalse(nc1.satisfiedByNetworkCapabilities(nc5));
+ assertFalse(nc5.satisfiedByNetworkCapabilities(nc1));
+ }
+
@Test
public void testWifiAwareNetworkSpecifier() {
final NetworkCapabilities nc = new NetworkCapabilities()
@@ -1109,7 +1107,7 @@
final TransportInfo transportInfo = new TransportInfo() {};
final String ssid = "TEST_SSID";
final String packageName = "com.google.test.networkcapabilities";
- final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ final NetworkCapabilities.Builder capBuilder = new NetworkCapabilities.Builder()
.addTransportType(TRANSPORT_WIFI)
.addTransportType(TRANSPORT_CELLULAR)
.removeTransportType(TRANSPORT_CELLULAR)
@@ -1125,8 +1123,14 @@
.setSignalStrength(signalStrength)
.setSsid(ssid)
.setRequestorUid(requestUid)
- .setRequestorPackageName(packageName)
- .build();
+ .setRequestorPackageName(packageName);
+ final Network network1 = new Network(100);
+ final Network network2 = new Network(101);
+ final List<Network> inputNetworks = List.of(network1, network2);
+ if (isAtLeastT()) {
+ capBuilder.setUnderlyingNetworks(inputNetworks);
+ }
+ final NetworkCapabilities nc = capBuilder.build();
assertEquals(1, nc.getTransportTypes().length);
assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]);
assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS));
@@ -1144,6 +1148,11 @@
assertEquals(ssid, nc.getSsid());
assertEquals(requestUid, nc.getRequestorUid());
assertEquals(packageName, nc.getRequestorPackageName());
+ if (isAtLeastT()) {
+ final List<Network> outputNetworks = nc.getUnderlyingNetworks();
+ assertEquals(network1, outputNetworks.get(0));
+ assertEquals(network2, outputNetworks.get(1));
+ }
// Cannot assign null into NetworkCapabilities.Builder
try {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null);
@@ -1169,12 +1178,12 @@
assertEquals(0, nc.getCapabilities().length);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
public void testRestrictCapabilitiesForTestNetworkByNotOwnerWithNonRestrictedNc() {
testRestrictCapabilitiesForTestNetworkWithNonRestrictedNc(false /* isOwner */);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
public void testRestrictCapabilitiesForTestNetworkByOwnerWithNonRestrictedNc() {
testRestrictCapabilitiesForTestNetworkWithNonRestrictedNc(true /* isOwner */);
}
@@ -1219,12 +1228,12 @@
assertEquals(expectedNcBuilder.build(), nonRestrictedNc);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
public void testRestrictCapabilitiesForTestNetworkByNotOwnerWithRestrictedNc() {
testRestrictCapabilitiesForTestNetworkWithRestrictedNc(false /* isOwner */);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
public void testRestrictCapabilitiesForTestNetworkByOwnerWithRestrictedNc() {
testRestrictCapabilitiesForTestNetworkWithRestrictedNc(true /* isOwner */);
}
diff --git a/tests/common/java/android/net/NetworkProviderTest.kt b/tests/common/java/android/net/NetworkProviderTest.kt
index ff5de1d..3ceacf8 100644
--- a/tests/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/common/java/android/net/NetworkProviderTest.kt
@@ -32,6 +32,7 @@
import androidx.test.InstrumentationRegistry
import com.android.net.module.util.ArrayTrackRecord
import com.android.testutils.CompatUtil
+import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -62,6 +63,7 @@
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.Q)
+@ConnectivityModuleTest
class NetworkProviderTest {
@Rule @JvmField
val mIgnoreRule = DevSdkIgnoreRule()
diff --git a/tests/common/java/android/net/NetworkSpecifierTest.kt b/tests/common/java/android/net/NetworkSpecifierTest.kt
index f3409f5..b960417 100644
--- a/tests/common/java/android/net/NetworkSpecifierTest.kt
+++ b/tests/common/java/android/net/NetworkSpecifierTest.kt
@@ -17,18 +17,20 @@
import android.os.Build
import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
-import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
-import org.junit.Test
-import org.junit.runner.RunWith
+import kotlin.test.assertTrue
@SmallTest
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.Q)
+@ConnectivityModuleTest
class NetworkSpecifierTest {
private class TestNetworkSpecifier(
val intData: Int = 123,
diff --git a/tests/common/java/android/net/NetworkStateSnapshotTest.kt b/tests/common/java/android/net/NetworkStateSnapshotTest.kt
index 0ca4d95..0dad6a8 100644
--- a/tests/common/java/android/net/NetworkStateSnapshotTest.kt
+++ b/tests/common/java/android/net/NetworkStateSnapshotTest.kt
@@ -22,9 +22,10 @@
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.os.Build
import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Test
import org.junit.runner.RunWith
import java.net.Inet4Address
@@ -59,6 +60,7 @@
@SmallTest
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@ConnectivityModuleTest
class NetworkStateSnapshotTest {
@Test
@@ -67,7 +69,7 @@
LinkProperties(), null, TYPE_NONE)
val snapshot = NetworkStateSnapshot(
Network(TEST_NETID), TEST_CAPABILITIES, TEST_LINK_PROPERTIES, TEST_IMSI, TYPE_WIFI)
- assertParcelSane(emptySnapshot, 5)
- assertParcelSane(snapshot, 5)
+ assertParcelingIsLossless(emptySnapshot)
+ assertParcelingIsLossless(snapshot)
}
}
diff --git a/tests/common/java/android/net/NetworkTest.java b/tests/common/java/android/net/NetworkTest.java
index 11d44b8..c102cb3 100644
--- a/tests/common/java/android/net/NetworkTest.java
+++ b/tests/common/java/android/net/NetworkTest.java
@@ -22,11 +22,18 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.os.Build;
import android.platform.test.annotations.AppModeFull;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.ConnectivityModuleTest;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,9 +47,13 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class NetworkTest {
final Network mNetwork = new Network(99);
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
@Test
public void testBindSocketOfInvalidFdThrows() throws Exception {
@@ -150,6 +161,39 @@
assertEquals(16290598925L, three.getNetworkHandle());
}
+ // getNetId() did not exist in Q
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testGetNetId() {
+ assertEquals(1234, new Network(1234).getNetId());
+ assertEquals(2345, new Network(2345, true).getNetId());
+ }
+
+ @Test
+ public void testFromNetworkHandle() {
+ final Network network = new Network(1234);
+ assertEquals(network.netId, Network.fromNetworkHandle(network.getNetworkHandle()).netId);
+ }
+
+ // Parsing private DNS bypassing handle was not supported until S
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testFromNetworkHandlePrivateDnsBypass_S() {
+ final Network network = new Network(1234, true);
+
+ final Network recreatedNetwork = Network.fromNetworkHandle(network.getNetworkHandle());
+ assertEquals(network.netId, recreatedNetwork.netId);
+ assertEquals(network.getNetIdForResolv(), recreatedNetwork.getNetIdForResolv());
+ }
+
+ @Test @IgnoreAfter(Build.VERSION_CODES.R)
+ public void testFromNetworkHandlePrivateDnsBypass_R() {
+ final Network network = new Network(1234, true);
+
+ final Network recreatedNetwork = Network.fromNetworkHandle(network.getNetworkHandle());
+ assertEquals(network.netId, recreatedNetwork.netId);
+ // Until R included, fromNetworkHandle would not parse the private DNS bypass flag
+ assertEquals(network.netId, recreatedNetwork.getNetIdForResolv());
+ }
+
@Test
public void testGetPrivateDnsBypassingCopy() {
final Network copy = mNetwork.getPrivateDnsBypassingCopy();
diff --git a/tests/common/java/android/net/OemNetworkPreferencesTest.java b/tests/common/java/android/net/OemNetworkPreferencesTest.java
index fd29a95..d96f80c 100644
--- a/tests/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/common/java/android/net/OemNetworkPreferencesTest.java
@@ -17,7 +17,7 @@
package android.net;
import static com.android.testutils.MiscAsserts.assertThrows;
-import static com.android.testutils.ParcelUtils.assertParcelSane;
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -27,6 +27,7 @@
import androidx.test.filters.SmallTest;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -38,6 +39,7 @@
@IgnoreUpTo(Build.VERSION_CODES.R)
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@ConnectivityModuleTest
public class OemNetworkPreferencesTest {
private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED;
@@ -101,7 +103,7 @@
final OemNetworkPreferences prefs = mBuilder.build();
- assertParcelSane(prefs, 1 /* fieldCount */);
+ assertParcelingIsLossless(prefs);
}
@Test
diff --git a/tests/common/java/android/net/RouteInfoTest.java b/tests/common/java/android/net/RouteInfoTest.java
index 71689f9..5b28b84 100644
--- a/tests/common/java/android/net/RouteInfoTest.java
+++ b/tests/common/java/android/net/RouteInfoTest.java
@@ -16,10 +16,11 @@
package android.net;
+import static android.net.RouteInfo.RTN_THROW;
+import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static com.android.testutils.MiscAsserts.assertEqualBothWays;
-import static com.android.testutils.MiscAsserts.assertFieldCountEquals;
import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
@@ -36,8 +37,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.Rule;
@@ -50,6 +51,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class RouteInfoTest {
@Rule
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
@@ -329,6 +331,16 @@
}
@Test
+ public void testRouteTypes() {
+ RouteInfo r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE);
+ assertEquals(RTN_UNREACHABLE, r.getType());
+ r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNICAST);
+ assertEquals(RTN_UNICAST, r.getType());
+ r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_THROW);
+ assertEquals(RTN_THROW, r.getType());
+ }
+
+ @Test
public void testTruncation() {
LinkAddress l;
RouteInfo r;
@@ -371,17 +383,6 @@
assertParcelingIsLossless(r);
}
- @Test @IgnoreAfter(Build.VERSION_CODES.Q)
- public void testFieldCount_Q() {
- assertFieldCountEquals(6, RouteInfo.class);
- }
-
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
- public void testFieldCount() {
- // Make sure any new field is covered by the above parceling tests when changing this number
- assertFieldCountEquals(7, RouteInfo.class);
- }
-
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testMtu() {
RouteInfo r;
diff --git a/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt b/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt
index 7a18bb0..063ea23 100644
--- a/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt
+++ b/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt
@@ -20,8 +20,7 @@
import android.os.Build
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
-import com.android.testutils.assertFieldCountEquals
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Test
import org.junit.runner.RunWith
import java.net.InetAddress
@@ -68,15 +67,11 @@
assertNotEquals(makeData(tcpWndScale = 3), makeData())
assertNotEquals(makeData(ipTos = 0x14), makeData())
assertNotEquals(makeData(ipTtl = 11), makeData())
-
- // Update above assertions if field is added
- assertFieldCountEquals(5, KeepalivePacketData::class.java)
- assertFieldCountEquals(6, TcpKeepalivePacketData::class.java)
}
@Test
fun testParcelUnparcel() {
- assertParcelSane(makeData(), fieldCount = 6) { a, b ->
+ assertParcelingIsLossless(makeData()) { a, b ->
// .equals() does not verify .packet
a == b && a.packet contentEquals b.packet
}
@@ -98,9 +93,5 @@
assertTrue(str.contains(data.getTcpWindowScale().toString()))
assertTrue(str.contains(data.getIpTos().toString()))
assertTrue(str.contains(data.getIpTtl().toString()))
-
- // Update above assertions if field is added
- assertFieldCountEquals(5, KeepalivePacketData::class.java)
- assertFieldCountEquals(6, TcpKeepalivePacketData::class.java)
}
}
\ No newline at end of file
diff --git a/tests/common/java/android/net/UidRangeTest.java b/tests/common/java/android/net/UidRangeTest.java
index 1b1c954..d46fdc9 100644
--- a/tests/common/java/android/net/UidRangeTest.java
+++ b/tests/common/java/android/net/UidRangeTest.java
@@ -22,15 +22,20 @@
import static android.os.UserHandle.getUid;
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 android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Build;
import android.os.UserHandle;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -38,8 +43,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class UidRangeTest {
/*
@@ -110,4 +118,61 @@
assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getStartUser());
assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getEndUser());
}
+
+ private static void assertSameUids(@NonNull final String msg, @Nullable final Set<UidRange> s1,
+ @Nullable final Set<UidRange> s2) {
+ assertTrue(msg + " : " + s1 + " unexpectedly different from " + s2,
+ UidRange.hasSameUids(s1, s2));
+ }
+
+ private static void assertDifferentUids(@NonNull final String msg,
+ @Nullable final Set<UidRange> s1, @Nullable final Set<UidRange> s2) {
+ assertFalse(msg + " : " + s1 + " unexpectedly equal to " + s2,
+ UidRange.hasSameUids(s1, s2));
+ }
+
+ // R doesn't have UidRange.hasSameUids, but since S has the module, it does have hasSameUids.
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testHasSameUids() {
+ final UidRange uids1 = new UidRange(1, 100);
+ final UidRange uids2 = new UidRange(3, 300);
+ final UidRange uids3 = new UidRange(1, 1000);
+ final UidRange uids4 = new UidRange(800, 1000);
+
+ assertSameUids("null <=> null", null, null);
+ final Set<UidRange> set1 = new ArraySet<>();
+ assertDifferentUids("empty <=> null", set1, null);
+ final Set<UidRange> set2 = new ArraySet<>();
+ set1.add(uids1);
+ assertDifferentUids("uids1 <=> null", set1, null);
+ assertDifferentUids("null <=> uids1", null, set1);
+ assertDifferentUids("uids1 <=> empty", set1, set2);
+ set2.add(uids1);
+ assertSameUids("uids1 <=> uids1", set1, set2);
+ set1.add(uids2);
+ assertDifferentUids("uids1,2 <=> uids1", set1, set2);
+ set1.add(uids3);
+ assertDifferentUids("uids1,2,3 <=> uids1", set1, set2);
+ set2.add(uids3);
+ assertDifferentUids("uids1,2,3 <=> uids1,3", set1, set2);
+ set2.add(uids2);
+ assertSameUids("uids1,2,3 <=> uids1,2,3", set1, set2);
+ set1.remove(uids2);
+ assertDifferentUids("uids1,3 <=> uids1,2,3", set1, set2);
+ set1.add(uids4);
+ assertDifferentUids("uids1,3,4 <=> uids1,2,3", set1, set2);
+ set2.add(uids4);
+ assertDifferentUids("uids1,3,4 <=> uids1,2,3,4", set1, set2);
+ assertDifferentUids("uids1,3,4 <=> null", set1, null);
+ set2.remove(uids2);
+ assertSameUids("uids1,3,4 <=> uids1,3,4", set1, set2);
+ set2.remove(uids1);
+ assertDifferentUids("uids1,3,4 <=> uids3,4", set1, set2);
+ set2.remove(uids3);
+ assertDifferentUids("uids1,3,4 <=> uids4", set1, set2);
+ set2.remove(uids4);
+ assertDifferentUids("uids1,3,4 <=> empty", set1, set2);
+ assertDifferentUids("null <=> empty", null, set2);
+ assertSameUids("empty <=> empty", set2, new ArraySet<>());
+ }
}
diff --git a/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt
index f23ba26..a041c4e 100644
--- a/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt
+++ b/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt
@@ -18,9 +18,10 @@
import android.os.Build
import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.assertEquals
@@ -32,6 +33,7 @@
@SmallTest
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@ConnectivityModuleTest
class UnderlyingNetworkInfoTest {
@Test
fun testParcelUnparcel() {
@@ -39,12 +41,12 @@
assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid())
assertEquals(TEST_IFACE, testInfo.getInterface())
assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces())
- assertParcelSane(testInfo, 3)
+ assertParcelingIsLossless(testInfo)
val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf())
assertEquals(0, emptyInfo.getOwnerUid())
assertEquals(String(), emptyInfo.getInterface())
assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces())
- assertParcelSane(emptyInfo, 3)
+ assertParcelingIsLossless(emptyInfo)
}
}
\ No newline at end of file
diff --git a/tests/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
index 88996d9..fa4adcb 100644
--- a/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
+++ b/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
@@ -16,7 +16,7 @@
package android.net.apf;
-import static com.android.testutils.ParcelUtils.assertParcelSane;
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -62,7 +62,7 @@
assertEquals(456, caps.maximumApfProgramSize);
assertEquals(789, caps.apfPacketFormat);
- assertParcelSane(caps, 3);
+ assertParcelingIsLossless(caps);
}
@Test
diff --git a/tests/common/java/android/net/metrics/ApfProgramEventTest.kt b/tests/common/java/android/net/metrics/ApfProgramEventTest.kt
index 0b7b740..1c175da 100644
--- a/tests/common/java/android/net/metrics/ApfProgramEventTest.kt
+++ b/tests/common/java/android/net/metrics/ApfProgramEventTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -48,7 +48,7 @@
assertEquals(5, apfProgramEvent.programLength)
assertEquals(ApfProgramEvent.flagsFor(true, true), apfProgramEvent.flags)
- assertParcelSane(apfProgramEvent, 6)
+ assertParcelingIsLossless(apfProgramEvent)
}
@Test
diff --git a/tests/common/java/android/net/metrics/ApfStatsTest.kt b/tests/common/java/android/net/metrics/ApfStatsTest.kt
index 46a8c8e..610e674 100644
--- a/tests/common/java/android/net/metrics/ApfStatsTest.kt
+++ b/tests/common/java/android/net/metrics/ApfStatsTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,6 +52,6 @@
assertEquals(8, apfStats.programUpdatesAllowingMulticast)
assertEquals(9, apfStats.maxProgramSize)
- assertParcelSane(apfStats, 10)
+ assertParcelingIsLossless(apfStats)
}
}
diff --git a/tests/common/java/android/net/metrics/DhcpClientEventTest.kt b/tests/common/java/android/net/metrics/DhcpClientEventTest.kt
index 8d7a9c4..4c70e11 100644
--- a/tests/common/java/android/net/metrics/DhcpClientEventTest.kt
+++ b/tests/common/java/android/net/metrics/DhcpClientEventTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,6 +38,6 @@
assertEquals(FAKE_MESSAGE, dhcpClientEvent.msg)
assertEquals(Integer.MAX_VALUE, dhcpClientEvent.durationMs)
- assertParcelSane(dhcpClientEvent, 2)
+ assertParcelingIsLossless(dhcpClientEvent)
}
}
diff --git a/tests/common/java/android/net/metrics/IpManagerEventTest.kt b/tests/common/java/android/net/metrics/IpManagerEventTest.kt
index 64be508..bb21dca 100644
--- a/tests/common/java/android/net/metrics/IpManagerEventTest.kt
+++ b/tests/common/java/android/net/metrics/IpManagerEventTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -33,7 +33,7 @@
assertEquals(it, ipManagerEvent.eventType)
assertEquals(Long.MAX_VALUE, ipManagerEvent.durationMs)
- assertParcelSane(ipManagerEvent, 2)
+ assertParcelingIsLossless(ipManagerEvent)
}
}
}
diff --git a/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt b/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt
index 55b5e49..3d21b81 100644
--- a/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt
+++ b/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,7 +32,7 @@
val ipReachabilityEvent = IpReachabilityEvent(it)
assertEquals(it, ipReachabilityEvent.eventType)
- assertParcelSane(ipReachabilityEvent, 1)
+ assertParcelingIsLossless(ipReachabilityEvent)
}
}
}
diff --git a/tests/common/java/android/net/metrics/NetworkEventTest.kt b/tests/common/java/android/net/metrics/NetworkEventTest.kt
index 41430b0..17b5e2d 100644
--- a/tests/common/java/android/net/metrics/NetworkEventTest.kt
+++ b/tests/common/java/android/net/metrics/NetworkEventTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@
assertEquals(it, networkEvent.eventType)
assertEquals(Long.MAX_VALUE, networkEvent.durationMs)
- assertParcelSane(networkEvent, 2)
+ assertParcelingIsLossless(networkEvent)
}
}
}
diff --git a/tests/common/java/android/net/metrics/RaEventTest.kt b/tests/common/java/android/net/metrics/RaEventTest.kt
index d9b7203..e9daa0f 100644
--- a/tests/common/java/android/net/metrics/RaEventTest.kt
+++ b/tests/common/java/android/net/metrics/RaEventTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -67,6 +67,6 @@
assertEquals(5, raEvent.rdnssLifetime)
assertEquals(6, raEvent.dnsslLifetime)
- assertParcelSane(raEvent, 6)
+ assertParcelingIsLossless(raEvent)
}
}
diff --git a/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt b/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt
index 51c0d41..7dfa7e1 100644
--- a/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt
+++ b/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
import java.lang.reflect.Modifier
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -51,7 +51,7 @@
assertTrue(validationProbeEvent.probeType hasType FIRST_VALIDATION)
assertEquals(ValidationProbeEvent.DNS_SUCCESS, validationProbeEvent.returnCode)
- assertParcelSane(validationProbeEvent, 3)
+ assertParcelingIsLossless(validationProbeEvent)
}
@Test
diff --git a/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt b/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt
index 7b22e45..c90b1aa 100644
--- a/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt
+++ b/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt
@@ -31,7 +31,6 @@
import android.os.Build
import androidx.test.filters.SmallTest
import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.assertFieldCountEquals
import com.android.testutils.assertNetworkStatsEquals
import com.android.testutils.assertParcelingIsLossless
import org.junit.Before
@@ -176,7 +175,6 @@
assertParcelingIsLossless(testStatsEmpty)
assertParcelingIsLossless(testStats1)
assertParcelingIsLossless(testStats2)
- assertFieldCountEquals(15, NetworkStats::class.java)
}
@Test
diff --git a/tests/cts/OWNERS b/tests/cts/OWNERS
index 4264345..8dfa455 100644
--- a/tests/cts/OWNERS
+++ b/tests/cts/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 31808
set noparent
-lorenzo@google.com
-satk@google.com
\ No newline at end of file
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp
index 3185f7e..b684068 100644
--- a/tests/cts/hostside/Android.bp
+++ b/tests/cts/hostside/Android.bp
@@ -25,9 +25,13 @@
"cts-tradefed",
"tradefed",
],
+ static_libs: [
+ "modules-utils-build-testing",
+ ],
// Tag this module as a cts test artifact
test_suites: [
"cts",
"general-tests",
+ "sts"
],
}
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index b7fefaf..7a73313 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -23,7 +23,10 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
<target_preparer class="com.android.cts.net.NetworkPolicyTestsPreparer" />
+ <!-- Enabling change id ALLOW_TEST_API_ACCESS allows that package to access @TestApi methods -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="am compat enable ALLOW_TEST_API_ACCESS com.android.cts.net.hostside.app2" />
+ <option name="teardown-command" value="am compat reset ALLOW_TEST_API_ACCESS com.android.cts.net.hostside.app2" />
<option name="teardown-command" value="cmd power set-mode 0" />
<option name="teardown-command" value="cmd battery reset" />
<option name="teardown-command" value="cmd netpolicy stop-watching" />
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
index f523745..28437c2 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -16,6 +16,8 @@
package com.android.cts.net.hostside;
+import android.app.job.JobInfo;
+
import com.android.cts.net.hostside.INetworkCallback;
interface IMyService {
@@ -26,4 +28,5 @@
void sendNotification(int notificationId, String notificationType);
void registerNetworkCallback(in NetworkRequest request, in INetworkCallback cb);
void unregisterNetworkCallback();
+ void scheduleJob(in JobInfo jobInfo);
}
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
index 165f530..19198c5 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
@@ -17,6 +17,10 @@
package com.android.cts.net.hostside;
interface INetworkStateObserver {
- boolean isForeground();
- void onNetworkStateChecked(String resultData);
+ void onNetworkStateChecked(int resultCode, String resultData);
+
+ const int RESULT_SUCCESS_NETWORK_STATE_CHECKED = 0;
+ const int RESULT_ERROR_UNEXPECTED_PROC_STATE = 1;
+ const int RESULT_ERROR_UNEXPECTED_CAPABILITIES = 2;
+ const int RESULT_ERROR_OTHER = 3;
}
\ No newline at end of file
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 112b9eb..12e7d33 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -18,20 +18,19 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-android_test_helper_app {
- name: "CtsHostsideNetworkTestsApp",
- defaults: ["cts_support_defaults"],
- //sdk_version: "current",
+java_defaults {
+ name: "CtsHostsideNetworkTestsAppDefaults",
platform_apis: true,
static_libs: [
- "androidx.test.rules",
+ "CtsHostsideNetworkTestsAidl",
"androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
"compatibility-device-util-axt",
"cts-net-utils",
"ctstestrunner-axt",
- "ub-uiautomator",
- "CtsHostsideNetworkTestsAidl",
"modules-utils-build",
+ "ub-uiautomator",
],
libs: [
"android.test.runner",
@@ -42,5 +41,31 @@
test_suites: [
"cts",
"general-tests",
+ "sts",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkTestsApp",
+ defaults: [
+ "cts_support_defaults",
+ "framework-connectivity-test-defaults",
+ "CtsHostsideNetworkTestsAppDefaults",
+ ],
+ static_libs: [
+ "NetworkStackApiStableShims",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkTestsAppNext",
+ defaults: [
+ "cts_support_defaults",
+ "framework-connectivity-test-defaults",
+ "CtsHostsideNetworkTestsAppDefaults",
+ "ConnectivityNextEnableDefaults",
+ ],
+ static_libs: [
+ "NetworkStackApiCurrentShims",
],
}
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index 3940de4..d56e5d4 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -15,42 +15,43 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.net.hostside">
+ package="com.android.cts.net.hostside">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
- <application android:requestLegacyExternalStorage="true" >
- <uses-library android:name="android.test.runner" />
- <activity android:name=".MyActivity" />
+ <application android:requestLegacyExternalStorage="true">
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".MyActivity"/>
<service android:name=".MyVpnService"
- android:permission="android.permission.BIND_VPN_SERVICE">
+ android:permission="android.permission.BIND_VPN_SERVICE"
+ android:exported="true">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
- <service
- android:name=".MyNotificationListenerService"
- android:label="MyNotificationListenerService"
- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
+ <service android:name=".MyNotificationListenerService"
+ android:label="MyNotificationListenerService"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:exported="true">
<intent-filter>
- <action android:name="android.service.notification.NotificationListenerService" />
+ <action android:name="android.service.notification.NotificationListenerService"/>
</intent-filter>
</service>
</application>
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.net.hostside" />
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.net.hostside"/>
</manifest>
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index f9e30b6..d9ff539 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -50,7 +50,7 @@
public final void tearDown() throws Exception {
super.tearDown();
- executeSilentShellCommand("cmd battery reset");
+ resetBatteryState();
setAppIdle(false);
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index 6f32c56..e0ce4ea 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -101,7 +101,7 @@
@Test
public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
throws Exception {
- setPendingIntentWhitelistDuration(NETWORK_TIMEOUT_MS);
+ setPendingIntentAllowlistDuration(NETWORK_TIMEOUT_MS);
try {
registerNotificationListenerService();
setDozeMode(true);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
new file mode 100644
index 0000000..a850e3b
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AbstractExpeditedJobTest extends AbstractRestrictBackgroundNetworkTestCase {
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+ resetDeviceState();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+ resetDeviceState();
+ }
+
+ private void resetDeviceState() throws Exception {
+ resetBatteryState();
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+
+ @Test
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ public void testNetworkAccess_batterySaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dataSaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({APP_STANDBY_MODE})
+ public void testNetworkAccess_appIdleState() throws Exception {
+ turnBatteryOn();
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE})
+ public void testNetworkAccess_dozeMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dataAndBatterySaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE, DATA_SAVER_MODE, METERED_NETWORK})
+ public void testNetworkAccess_dozeAndDataSaverMode() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK, DOZE_MODE,
+ APP_STANDBY_MODE})
+ public void testNetworkAccess_allRestrictionsEnabled() throws Exception {
+ assertBackgroundNetworkAccess(true);
+ assertExpeditedJobHasNetworkAccess();
+
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+ setAppIdle(true);
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+ assertExpeditedJobHasNoNetworkAccess();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index defea4c..1b52ec4 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -22,6 +22,7 @@
import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation;
@@ -39,6 +40,7 @@
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.NotificationManager;
+import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -52,13 +54,19 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.SystemClock;
+import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.util.Log;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.BatteryUtils;
+import com.android.compatibility.common.util.DeviceConfigStateHelper;
import org.junit.Rule;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -75,12 +83,25 @@
private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity";
private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService";
+ private static final String TEST_APP2_JOB_SERVICE_CLASS = TEST_APP2_PKG + ".MyJobService";
+
+ private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(
+ TEST_APP2_PKG, TEST_APP2_JOB_SERVICE_CLASS);
+
+ private static final int TEST_JOB_ID = 7357437;
private static final int SLEEP_TIME_SEC = 1;
// Constants below must match values defined on app2's Common.java
private static final String MANIFEST_RECEIVER = "ManifestReceiver";
private static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+ private static final String ACTION_FINISH_ACTIVITY =
+ "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
+ private static final String ACTION_FINISH_JOB =
+ "com.android.cts.net.hostside.app2.action.FINISH_JOB";
+ // Copied from com.android.server.net.NetworkPolicyManagerService class
+ private static final String ACTION_SNOOZE_WARNING =
+ "com.android.server.net.action.SNOOZE_WARNING";
private static final String ACTION_RECEIVER_READY =
"com.android.cts.net.hostside.app2.action.RECEIVER_READY";
@@ -102,19 +123,23 @@
private static final String NETWORK_STATUS_SEPARATOR = "\\|";
private static final int SECOND_IN_MS = 1000;
static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
+
private static int PROCESS_STATE_FOREGROUND_SERVICE;
private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+ private static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
private static final String EMPTY_STRING = "";
protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
+ protected static final int TYPE_EXPEDITED_JOB = 2;
private static final int BATTERY_STATE_TIMEOUT_MS = 5000;
private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 500;
- private static final int FOREGROUND_PROC_NETWORK_TIMEOUT_MS = 6000;
+ private static final int ACTIVITY_NETWORK_STATE_TIMEOUT_MS = 6_000;
+ private static final int JOB_NETWORK_STATE_TIMEOUT_MS = 10_000;
// Must be higher than NETWORK_TIMEOUT_MS
private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4;
@@ -126,30 +151,33 @@
protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 5_000; // 5 sec
+ private static final long BROADCAST_TIMEOUT_MS = 15_000;
+
protected Context mContext;
protected Instrumentation mInstrumentation;
protected ConnectivityManager mCm;
protected int mUid;
private int mMyUid;
private MyServiceClient mServiceClient;
- private String mDeviceIdleConstantsSetting;
+ private DeviceConfigStateHelper mDeviceIdleDeviceConfigStateHelper;
@Rule
public final RuleChain mRuleChain = RuleChain.outerRule(new RequiredPropertiesRule())
.around(new MeterednessConfigurationRule());
protected void setUp() throws Exception {
-
+ // TODO: Annotate these constants with @TestApi instead of obtaining them using reflection
PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class
.getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null);
mInstrumentation = getInstrumentation();
mContext = getContext();
mCm = getConnectivityManager();
+ mDeviceIdleDeviceConfigStateHelper =
+ new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_DEVICE_IDLE);
mUid = getUid(TEST_APP2_PKG);
mMyUid = getUid(mContext.getPackageName());
mServiceClient = new MyServiceClient(mContext);
mServiceClient.bind();
- mDeviceIdleConstantsSetting = "device_idle_constants";
executeShellCommand("cmd netpolicy start-watching " + mUid);
setAppIdle(false);
@@ -194,6 +222,12 @@
+ maxAttempts * SLEEP_TIME_SEC + " seconds", expectedCount, count);
}
+ protected void assertSnoozeWarningNotReceived() throws Exception {
+ // Wait for a while to take broadcast queue delays into account
+ SystemClock.sleep(BROADCAST_TIMEOUT_MS);
+ assertEquals(0, getNumberBroadcastsReceived(DYNAMIC_RECEIVER, ACTION_SNOOZE_WARNING));
+ }
+
protected String sendOrderedBroadcast(Intent intent) throws Exception {
return sendOrderedBroadcast(intent, ORDERED_BROADCAST_TIMEOUT_MS);
}
@@ -258,8 +292,8 @@
/**
* Asserts that an app always have access while on foreground or running a foreground service.
*
- * <p>This method will launch an activity and a foreground service to make the assertion, but
- * will finish the activity / stop the service afterwards.
+ * <p>This method will launch an activity, a foreground service to make
+ * the assertion, but will finish the activity / stop the service afterwards.
*/
protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{
// Checks foreground first.
@@ -271,6 +305,16 @@
stopForegroundService();
}
+ protected void assertExpeditedJobHasNetworkAccess() throws Exception {
+ launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB);
+ finishExpeditedJob();
+ }
+
+ protected void assertExpeditedJobHasNoNetworkAccess() throws Exception {
+ launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB, false);
+ finishExpeditedJob();
+ }
+
protected final void assertBackgroundState() throws Exception {
final int maxTries = 30;
ProcessState state = null;
@@ -342,7 +386,7 @@
for (int i = 1; i <= maxTries; i++) {
error = checkNetworkAccess(expectAvailable);
- if (error.isEmpty()) return;
+ if (error == null) return;
// TODO: ideally, it should retry only when it cannot connect to an external site,
// or no retry at all! But, currently, the initial change fails almost always on
@@ -414,7 +458,7 @@
errors.append("\tnetworkInfo: " + networkInfo + "\n");
errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n");
}
- return errors.toString();
+ return errors.length() == 0 ? null : errors.toString();
}
/**
@@ -606,6 +650,10 @@
assertBatteryState(true);
}
+ protected void resetBatteryState() {
+ BatteryUtils.runDumpsysBatteryReset();
+ }
+
private void assertBatteryState(boolean pluggedIn) throws Exception {
final long endTime = SystemClock.elapsedRealtime() + BATTERY_STATE_TIMEOUT_MS;
while (isDevicePluggedIn() != pluggedIn && SystemClock.elapsedRealtime() <= endTime) {
@@ -741,18 +789,21 @@
nm.isNotificationListenerAccessGranted(listenerComponent));
}
- protected void setPendingIntentWhitelistDuration(int durationMs) throws Exception {
- executeSilentShellCommand(String.format(
- "settings put global %s %s=%d", mDeviceIdleConstantsSetting,
- "notification_whitelist_duration", durationMs));
+ protected void setPendingIntentAllowlistDuration(long durationMs) {
+ mDeviceIdleDeviceConfigStateHelper.set("notification_allowlist_duration_ms",
+ String.valueOf(durationMs));
}
- protected void resetDeviceIdleSettings() throws Exception {
- executeShellCommand(String.format("settings delete global %s",
- mDeviceIdleConstantsSetting));
+ protected void resetDeviceIdleSettings() {
+ mDeviceIdleDeviceConfigStateHelper.restoreOriginalValues();
}
protected void launchComponentAndAssertNetworkAccess(int type) throws Exception {
+ launchComponentAndAssertNetworkAccess(type, true);
+ }
+
+ protected void launchComponentAndAssertNetworkAccess(int type, boolean expectAvailable)
+ throws Exception {
if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
startForegroundService();
assertForegroundServiceNetworkAccess();
@@ -764,21 +815,63 @@
final CountDownLatch latch = new CountDownLatch(1);
final Intent launchIntent = getIntentForComponent(type);
final Bundle extras = new Bundle();
- final String[] errors = new String[]{null};
- extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors));
+ final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1);
+ extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
+ extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
launchIntent.putExtras(extras);
mContext.startActivity(launchIntent);
- if (latch.await(FOREGROUND_PROC_NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- if (!errors[0].isEmpty()) {
- if (errors[0] == APP_NOT_FOREGROUND_ERROR) {
- // App didn't come to foreground when the activity is started, so try again.
- assertForegroundNetworkAccess();
- } else {
- fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
+ if (latch.await(ACTIVITY_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ final int resultCode = result.get(0).first;
+ final String resultData = result.get(0).second;
+ if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
+ final String error = checkForAvailabilityInResultData(
+ resultData, expectAvailable);
+ if (error != null) {
+ fail("Network is not available for activity in app2 (" + mUid + "): "
+ + error);
}
+ } else if (resultCode == INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE) {
+ Log.d(TAG, resultData);
+ // App didn't come to foreground when the activity is started, so try again.
+ assertForegroundNetworkAccess();
+ } else {
+ fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]");
}
} else {
- fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
+ fail("Timed out waiting for network availability status from app2's activity ("
+ + mUid + ")");
+ }
+ } else if (type == TYPE_EXPEDITED_JOB) {
+ final Bundle extras = new Bundle();
+ final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1);
+ final CountDownLatch latch = new CountDownLatch(1);
+ extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result));
+ extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable);
+ final JobInfo jobInfo = new JobInfo.Builder(TEST_JOB_ID, TEST_JOB_COMPONENT)
+ .setExpedited(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .setTransientExtras(extras)
+ .build();
+ mServiceClient.scheduleJob(jobInfo);
+ forceRunJob(TEST_APP2_PKG, TEST_JOB_ID);
+ if (latch.await(JOB_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ final int resultCode = result.get(0).first;
+ final String resultData = result.get(0).second;
+ if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) {
+ final String error = checkForAvailabilityInResultData(
+ resultData, expectAvailable);
+ if (error != null) {
+ Log.d(TAG, "Network state is unexpected, checking again. " + error);
+ // Right now we could end up in an unexpected state if expedited job
+ // doesn't have network access immediately after starting, so check again.
+ assertNetworkAccess(expectAvailable, false /* needScreenOn */);
+ }
+ } else {
+ fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]");
+ }
+ } else {
+ fail("Timed out waiting for network availability status from app2's expedited job ("
+ + mUid + ")");
}
} else {
throw new IllegalArgumentException("Unknown type: " + type);
@@ -812,36 +905,34 @@
}
private Binder getNewNetworkStateObserver(final CountDownLatch latch,
- final String[] errors) {
+ final ArrayList<Pair<Integer, String>> result) {
return new INetworkStateObserver.Stub() {
@Override
- public boolean isForeground() {
- try {
- final ProcessState state = getProcessStateByUid(mUid);
- return !isBackground(state.state);
- } catch (Exception e) {
- Log.d(TAG, "Error while reading the proc state for " + mUid + ": " + e);
- return false;
- }
- }
-
- @Override
- public void onNetworkStateChecked(String resultData) {
- errors[0] = resultData == null
- ? APP_NOT_FOREGROUND_ERROR
- : checkForAvailabilityInResultData(resultData, true);
+ public void onNetworkStateChecked(int resultCode, String resultData) {
+ result.add(Pair.create(resultCode, resultData));
latch.countDown();
}
};
}
/**
- * Finishes an activity on app2 so its process is demoted fromforeground status.
+ * Finishes an activity on app2 so its process is demoted from foreground status.
*/
protected void finishActivity() throws Exception {
- executeShellCommand("am broadcast -a "
- + " com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY "
- + "--receiver-foreground --receiver-registered-only");
+ final Intent intent = new Intent(ACTION_FINISH_ACTIVITY)
+ .setPackage(TEST_APP2_PKG)
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ sendOrderedBroadcast(intent);
+ }
+
+ /**
+ * Finishes the expedited job on app2 so its process is demoted from foreground status.
+ */
+ private void finishExpeditedJob() throws Exception {
+ final Intent intent = new Intent(ACTION_FINISH_JOB)
+ .setPackage(TEST_APP2_PKG)
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ sendOrderedBroadcast(intent);
}
protected void sendNotification(int notificationId, String notificationType) throws Exception {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
new file mode 100644
index 0000000..b2e81ff
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.clearSnoozeTimestamps;
+
+import android.content.pm.PackageManager;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.UiAutomatorUtils;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Period;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+public class DataWarningReceiverTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ clearSnoozeTimestamps();
+ registerBroadcastReceiver();
+ turnScreenOn();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testSnoozeWarningNotReceived() throws Exception {
+ Assume.assumeTrue("Feature not supported: " + PackageManager.FEATURE_TELEPHONY,
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+ final int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ Assume.assumeTrue("Valid subId not found",
+ subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ setSubPlanOwner(subId, TEST_PKG);
+ final List<SubscriptionPlan> originalPlans = sm.getSubscriptionPlans(subId);
+ try {
+ // In NetworkPolicyManagerService class, we set the data warning bytes to 90% of
+ // data limit bytes. So, create the subscription plan in such a way this data warning
+ // threshold is already reached.
+ final SubscriptionPlan plan = SubscriptionPlan.Builder
+ .createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
+ Period.ofMonths(1))
+ .setTitle("CTS")
+ .setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
+ .setDataUsage(999_000_000, System.currentTimeMillis())
+ .build();
+ sm.setSubscriptionPlans(subId, Arrays.asList(plan));
+ final UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
+ uiDevice.openNotification();
+ try {
+ final UiObject2 uiObject = UiAutomatorUtils.waitFindObject(
+ By.text("Data warning"));
+ Assume.assumeNotNull(uiObject);
+ uiObject.wait(Until.clickable(true), 10_000L);
+ uiObject.getParent().swipe(Direction.RIGHT, 1.0f);
+ } catch (Throwable t) {
+ Assume.assumeNoException(
+ "Error occurred while finding and swiping the notification", t);
+ }
+ assertSnoozeWarningNotReceived();
+ uiDevice.pressHome();
+ } finally {
+ sm.setSubscriptionPlans(subId, originalPlans);
+ setSubPlanOwner(subId, "");
+ }
+ }
+
+ private static void setSubPlanOwner(int subId, String packageName) throws Exception {
+ SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+ "cmd netpolicy set sub-plan-owner " + subId + " " + packageName);
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
index 66cb935..cb0341c 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -25,6 +25,7 @@
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
import com.android.compatibility.common.util.OnFailureRule;
@@ -52,7 +53,8 @@
}
prepareDumpRootDir();
- final File dumpFile = new File(mDumpDir, "dump-" + getShortenedTestName(description));
+ final String shortenedTestName = getShortenedTestName(description);
+ final File dumpFile = new File(mDumpDir, "dump-" + shortenedTestName);
Log.i(TAG, "Dumping debug info for " + description + ": " + dumpFile.getPath());
try (FileOutputStream out = new FileOutputStream(dumpFile)) {
for (String cmd : new String[] {
@@ -68,6 +70,16 @@
} catch (IOException e) {
Log.e(TAG, "Error closing file: " + dumpFile, e);
}
+ final UiDevice uiDevice = UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation());
+ final File screenshotFile = new File(mDumpDir, "sc-" + shortenedTestName + ".png");
+ uiDevice.takeScreenshot(screenshotFile);
+ final File windowHierarchyFile = new File(mDumpDir, "wh-" + shortenedTestName + ".xml");
+ try {
+ uiDevice.dumpWindowHierarchy(windowHierarchyFile);
+ } catch (IOException e) {
+ Log.e(TAG, "Error dumping window hierarchy", e);
+ }
}
private String getShortenedTestName(Description description) {
diff --git a/Tethering/src/android/net/util/TetheringMessageBase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java
similarity index 65%
copy from Tethering/src/android/net/util/TetheringMessageBase.java
copy to tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java
index 29c0a81..3809534 100644
--- a/Tethering/src/android/net/util/TetheringMessageBase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobMeteredTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
-/**
- * This class defines Message.what base addresses for various state machine.
- */
-public class TetheringMessageBase {
- public static final int BASE_MAIN_SM = 0;
- public static final int BASE_IPSERVER = 100;
+package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
+public class ExpeditedJobMeteredTest extends AbstractExpeditedJobTest {
}
diff --git a/Tethering/src/android/net/util/TetheringMessageBase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java
similarity index 65%
copy from Tethering/src/android/net/util/TetheringMessageBase.java
copy to tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java
index 29c0a81..6596269 100644
--- a/Tethering/src/android/net/util/TetheringMessageBase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ExpeditedJobNonMeteredTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
-/**
- * This class defines Message.what base addresses for various state machine.
- */
-public class TetheringMessageBase {
- public static final int BASE_MAIN_SM = 0;
- public static final int BASE_IPSERVER = 100;
+package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class ExpeditedJobNonMeteredTest extends AbstractExpeditedJobTest {
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
index c37e8d5..8b70f9b 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -16,6 +16,7 @@
package com.android.cts.net.hostside;
+import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -105,4 +106,8 @@
public void unregisterNetworkCallback() throws RemoteException {
mService.unregisterNetworkCallback();
}
+
+ public void scheduleJob(JobInfo jobInfo) throws RemoteException {
+ mService.scheduleJob(jobInfo);
+ }
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
index 7d3d4fc..449454e 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
@@ -17,18 +17,27 @@
package com.android.cts.net.hostside;
import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.IpPrefix;
import android.net.Network;
+import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.networkstack.apishim.VpnServiceBuilderShimImpl;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
+import com.android.networkstack.apishim.common.VpnServiceBuilderShim;
import java.io.IOException;
import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
public class MyVpnService extends VpnService {
@@ -38,6 +47,9 @@
public static final String ACTION_ESTABLISHED = "com.android.cts.net.hostside.ESTABNLISHED";
public static final String EXTRA_ALWAYS_ON = "is-always-on";
public static final String EXTRA_LOCKDOWN_ENABLED = "is-lockdown-enabled";
+ public static final String CMD_CONNECT = "connect";
+ public static final String CMD_DISCONNECT = "disconnect";
+ public static final String CMD_UPDATE_UNDERLYING_NETWORKS = "update_underlying_networks";
private ParcelFileDescriptor mFd = null;
private PacketReflector mPacketReflector = null;
@@ -46,48 +58,80 @@
public int onStartCommand(Intent intent, int flags, int startId) {
String packageName = getPackageName();
String cmd = intent.getStringExtra(packageName + ".cmd");
- if ("disconnect".equals(cmd)) {
+ if (CMD_DISCONNECT.equals(cmd)) {
stop();
- } else if ("connect".equals(cmd)) {
+ } else if (CMD_CONNECT.equals(cmd)) {
start(packageName, intent);
+ } else if (CMD_UPDATE_UNDERLYING_NETWORKS.equals(cmd)) {
+ updateUnderlyingNetworks(packageName, intent);
}
return START_NOT_STICKY;
}
- private void start(String packageName, Intent intent) {
- Builder builder = new Builder();
+ private void updateUnderlyingNetworks(String packageName, Intent intent) {
+ final ArrayList<Network> underlyingNetworks =
+ intent.getParcelableArrayListExtra(packageName + ".underlyingNetworks");
+ setUnderlyingNetworks(
+ (underlyingNetworks != null) ? underlyingNetworks.toArray(new Network[0]) : null);
+ }
- String addresses = intent.getStringExtra(packageName + ".addresses");
- if (addresses != null) {
- String[] addressArray = addresses.split(",");
- for (int i = 0; i < addressArray.length; i++) {
- String[] prefixAndMask = addressArray[i].split("/");
- try {
- InetAddress address = InetAddress.getByName(prefixAndMask[0]);
- int prefixLength = Integer.parseInt(prefixAndMask[1]);
- builder.addAddress(address, prefixLength);
- } catch (UnknownHostException|NumberFormatException|
- ArrayIndexOutOfBoundsException e) {
- continue;
- }
- }
+ private String parseIpAndMaskListArgument(String packageName, Intent intent, String argName,
+ BiConsumer<InetAddress, Integer> consumer) {
+ final String addresses = intent.getStringExtra(packageName + "." + argName);
+
+ if (TextUtils.isEmpty(addresses)) {
+ return null;
}
- String routes = intent.getStringExtra(packageName + ".routes");
- if (routes != null) {
- String[] routeArray = routes.split(",");
- for (int i = 0; i < routeArray.length; i++) {
- String[] prefixAndMask = routeArray[i].split("/");
+ final String[] addressesArray = addresses.split(",");
+ for (String address : addressesArray) {
+ final Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
+ consumer.accept(ipAndMask.first, ipAndMask.second);
+ }
+
+ return addresses;
+ }
+
+ private String parseIpPrefixListArgument(String packageName, Intent intent, String argName,
+ Consumer<IpPrefix> consumer) {
+ return parseIpAndMaskListArgument(packageName, intent, argName,
+ (inetAddress, prefixLength) -> consumer.accept(
+ new IpPrefix(inetAddress, prefixLength)));
+ }
+
+ private void start(String packageName, Intent intent) {
+ Builder builder = new Builder();
+ VpnServiceBuilderShim vpnServiceBuilderShim = VpnServiceBuilderShimImpl.newInstance();
+
+ final String addresses = parseIpAndMaskListArgument(packageName, intent, "addresses",
+ builder::addAddress);
+
+ String addedRoutes;
+ if (SdkLevel.isAtLeastT() && intent.getBooleanExtra(packageName + ".addRoutesByIpPrefix",
+ false)) {
+ addedRoutes = parseIpPrefixListArgument(packageName, intent, "routes", (prefix) -> {
try {
- InetAddress address = InetAddress.getByName(prefixAndMask[0]);
- int prefixLength = Integer.parseInt(prefixAndMask[1]);
- builder.addRoute(address, prefixLength);
- } catch (UnknownHostException|NumberFormatException|
- ArrayIndexOutOfBoundsException e) {
- continue;
+ vpnServiceBuilderShim.addRoute(builder, prefix);
+ } catch (UnsupportedApiLevelException e) {
+ throw new RuntimeException(e);
}
- }
+ });
+ } else {
+ addedRoutes = parseIpAndMaskListArgument(packageName, intent, "routes",
+ builder::addRoute);
+ }
+
+ String excludedRoutes = null;
+ if (SdkLevel.isAtLeastT()) {
+ excludedRoutes = parseIpPrefixListArgument(packageName, intent, "excludedRoutes",
+ (prefix) -> {
+ try {
+ vpnServiceBuilderShim.excludeRoute(builder, prefix);
+ } catch (UnsupportedApiLevelException e) {
+ throw new RuntimeException(e);
+ }
+ });
}
String allowed = intent.getStringExtra(packageName + ".allowedapplications");
@@ -140,7 +184,8 @@
Log.i(TAG, "Establishing VPN,"
+ " addresses=" + addresses
- + " routes=" + routes
+ + " addedRoutes=" + addedRoutes
+ + " excludedRoutes=" + excludedRoutes
+ " allowedApplications=" + allowed
+ " disallowedApplications=" + disallowed);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
index 05576cc..89a9bd6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -49,6 +49,7 @@
import android.net.wifi.WifiManager.ActionListener;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.UserHandle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.data.ApnSetting;
@@ -140,6 +141,12 @@
return am.isLowRamDevice();
}
+ /** Forces JobScheduler to run the job if constraints are met. */
+ public static void forceRunJob(String pkg, int jobId) {
+ executeShellCommand("cmd jobscheduler run -f -u " + UserHandle.myUserId()
+ + " " + pkg + " " + jobId);
+ }
+
public static boolean isLocationEnabled() {
final LocationManager lm = (LocationManager) getContext().getSystemService(
Context.LOCATION_SERVICE);
@@ -370,6 +377,10 @@
}
}
+ public static void clearSnoozeTimestamps() {
+ executeShellCommand("dumpsys netpolicy --unsnooze");
+ }
+
public static String executeShellCommand(String command) {
final String result = runShellCommand(command).trim();
Log.d(TAG, "Output of '" + command + "': '" + result + "'");
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 215f129..5778b0d 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -17,6 +17,8 @@
package com.android.cts.net.hostside;
import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.os.Process.INVALID_UID;
@@ -30,9 +32,21 @@
import static android.system.OsConstants.SOCK_DGRAM;
import static android.test.MoreAsserts.assertNotEqual;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.testutils.Cleanup.testAndCleanup;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.annotation.Nullable;
+import android.app.Activity;
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
@@ -56,6 +70,7 @@
import android.net.VpnManager;
import android.net.VpnService;
import android.net.VpnTransportInfo;
+import android.net.cts.util.CtsNetUtils;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Looper;
@@ -71,15 +86,26 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.StructPollfd;
-import android.test.InstrumentationTestCase;
+import android.telephony.TelephonyManager;
import android.test.MoreAsserts;
import android.text.TextUtils;
import android.util.Log;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
import com.android.modules.utils.build.SdkLevel;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.RecorderCallback;
import com.android.testutils.TestableNetworkCallback;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -127,7 +153,8 @@
* https://source.android.com/devices/tech/config/kernel_network_tests.html
*
*/
-public class VpnTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class VpnTest {
// These are neither public nor @TestApi.
// TODO: add them to @TestApi.
@@ -135,6 +162,7 @@
private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
private static final String PRIVATE_DNS_SPECIFIER_SETTING = "private_dns_specifier";
+ private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
public static String TAG = "VpnTest";
public static int TIMEOUT_MS = 3 * 1000;
@@ -147,6 +175,9 @@
private ConnectivityManager mCM;
private WifiManager mWifiManager;
private RemoteSocketFactoryClient mRemoteSocketFactoryClient;
+ private CtsNetUtils mCtsNetUtils;
+ private PackageManager mPackageManager;
+ private TelephonyManager mTelephonyManager;
Network mNetwork;
NetworkCallback mCallback;
@@ -156,41 +187,55 @@
private String mOldPrivateDnsMode;
private String mOldPrivateDnsSpecifier;
+ @Rule
+ public final DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+
private boolean supportedHardware() {
final PackageManager pm = getInstrumentation().getContext().getPackageManager();
return !pm.hasSystemFeature("android.hardware.type.watch");
}
- @Override
- public void setUp() throws Exception {
- super.setUp();
+ public final <T extends Activity> T launchActivity(String packageName, Class<T> activityClass) {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(packageName, activityClass.getName());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final T activity = (T) getInstrumentation().startActivitySync(intent);
+ getInstrumentation().waitForIdleSync();
+ return activity;
+ }
+ @Before
+ public void setUp() throws Exception {
mNetwork = null;
mCallback = null;
storePrivateDnsSetting();
mDevice = UiDevice.getInstance(getInstrumentation());
mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
- MyActivity.class, null);
+ MyActivity.class);
mPackageName = mActivity.getPackageName();
mCM = (ConnectivityManager) mActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiManager = (WifiManager) mActivity.getSystemService(Context.WIFI_SERVICE);
mRemoteSocketFactoryClient = new RemoteSocketFactoryClient(mActivity);
mRemoteSocketFactoryClient.bind();
mDevice.waitForIdle();
+ mCtsNetUtils = new CtsNetUtils(getInstrumentation().getContext());
+ mPackageManager = getInstrumentation().getContext().getPackageManager();
+ mTelephonyManager =
+ getInstrumentation().getContext().getSystemService(TelephonyManager.class);
}
- @Override
+ @After
public void tearDown() throws Exception {
restorePrivateDnsSetting();
mRemoteSocketFactoryClient.unbind();
if (mCallback != null) {
mCM.unregisterNetworkCallback(mCallback);
}
+ mCtsNetUtils.tearDown();
Log.i(TAG, "Stopping VPN");
stopVpn();
mActivity.finish();
- super.tearDown();
}
private void prepareVpn() throws Exception {
@@ -244,11 +289,63 @@
}
}
+ private void updateUnderlyingNetworks(@Nullable ArrayList<Network> underlyingNetworks)
+ throws Exception {
+ final Intent intent = new Intent(mActivity, MyVpnService.class)
+ .putExtra(mPackageName + ".cmd", MyVpnService.CMD_UPDATE_UNDERLYING_NETWORKS)
+ .putParcelableArrayListExtra(
+ mPackageName + ".underlyingNetworks", underlyingNetworks);
+ mActivity.startService(intent);
+ }
+
+ private void establishVpn(String[] addresses, String[] routes, String[] excludedRoutes,
+ String allowedApplications, String disallowedApplications,
+ @Nullable ProxyInfo proxyInfo, @Nullable ArrayList<Network> underlyingNetworks,
+ boolean isAlwaysMetered, boolean addRoutesByIpPrefix)
+ throws Exception {
+ final Intent intent = new Intent(mActivity, MyVpnService.class)
+ .putExtra(mPackageName + ".cmd", MyVpnService.CMD_CONNECT)
+ .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses))
+ .putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
+ .putExtra(mPackageName + ".excludedRoutes", TextUtils.join(",", excludedRoutes))
+ .putExtra(mPackageName + ".allowedapplications", allowedApplications)
+ .putExtra(mPackageName + ".disallowedapplications", disallowedApplications)
+ .putExtra(mPackageName + ".httpProxy", proxyInfo)
+ .putParcelableArrayListExtra(
+ mPackageName + ".underlyingNetworks", underlyingNetworks)
+ .putExtra(mPackageName + ".isAlwaysMetered", isAlwaysMetered)
+ .putExtra(mPackageName + ".addRoutesByIpPrefix", addRoutesByIpPrefix);
+ mActivity.startService(intent);
+ }
+
// TODO: Consider replacing arguments with a Builder.
private void startVpn(
- String[] addresses, String[] routes, String allowedApplications,
- String disallowedApplications, @Nullable ProxyInfo proxyInfo,
- @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered) throws Exception {
+ String[] addresses, String[] routes, String allowedApplications,
+ String disallowedApplications, @Nullable ProxyInfo proxyInfo,
+ @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)
+ throws Exception {
+ startVpn(addresses, routes, new String[0] /* excludedRoutes */, allowedApplications,
+ disallowedApplications, proxyInfo, underlyingNetworks, isAlwaysMetered);
+ }
+
+ private void startVpn(
+ String[] addresses, String[] routes, String[] excludedRoutes,
+ String allowedApplications, String disallowedApplications,
+ @Nullable ProxyInfo proxyInfo,
+ @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)
+ throws Exception {
+ startVpn(addresses, routes, new String[0] /* excludedRoutes */, allowedApplications,
+ disallowedApplications, proxyInfo, underlyingNetworks, isAlwaysMetered,
+ false /* addRoutesByIpPrefix */);
+ }
+
+ private void startVpn(
+ String[] addresses, String[] routes, String[] excludedRoutes,
+ String allowedApplications, String disallowedApplications,
+ @Nullable ProxyInfo proxyInfo,
+ @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered,
+ boolean addRoutesByIpPrefix)
+ throws Exception {
prepareVpn();
// Register a callback so we will be notified when our VPN comes up.
@@ -269,18 +366,8 @@
mCM.registerNetworkCallback(request, mCallback); // Unregistered in tearDown.
// Start the service and wait up for TIMEOUT_MS ms for the VPN to come up.
- Intent intent = new Intent(mActivity, MyVpnService.class)
- .putExtra(mPackageName + ".cmd", "connect")
- .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses))
- .putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
- .putExtra(mPackageName + ".allowedapplications", allowedApplications)
- .putExtra(mPackageName + ".disallowedapplications", disallowedApplications)
- .putExtra(mPackageName + ".httpProxy", proxyInfo)
- .putParcelableArrayListExtra(
- mPackageName + ".underlyingNetworks", underlyingNetworks)
- .putExtra(mPackageName + ".isAlwaysMetered", isAlwaysMetered);
-
- mActivity.startService(intent);
+ establishVpn(addresses, routes, excludedRoutes, allowedApplications, disallowedApplications,
+ proxyInfo, underlyingNetworks, isAlwaysMetered, addRoutesByIpPrefix);
synchronized (mLock) {
if (mNetwork == null) {
Log.i(TAG, "bf mLock");
@@ -322,7 +409,7 @@
// and stopping a bound service has no effect. Instead, "start" the service again with an
// Intent that tells it to disconnect.
Intent intent = new Intent(mActivity, MyVpnService.class)
- .putExtra(mPackageName + ".cmd", "disconnect");
+ .putExtra(mPackageName + ".cmd", MyVpnService.CMD_DISCONNECT);
mActivity.startService(intent);
synchronized (mLockShutdown) {
try {
@@ -500,6 +587,12 @@
}
private void checkUdpEcho(String to, String expectedFrom) throws IOException {
+ checkUdpEcho(to, expectedFrom, expectedFrom != null);
+ }
+
+ private void checkUdpEcho(String to, String expectedFrom,
+ boolean expectConnectionOwnerIsVisible)
+ throws IOException {
DatagramSocket s;
InetAddress address = InetAddress.getByName(to);
if (address instanceof Inet6Address) { // http://b/18094870
@@ -523,7 +616,7 @@
try {
if (expectedFrom != null) {
s.send(p);
- checkConnectionOwnerUidUdp(s, true);
+ checkConnectionOwnerUidUdp(s, expectConnectionOwnerIsVisible);
s.receive(p);
MoreAsserts.assertEquals(data, p.getData());
} else {
@@ -532,7 +625,7 @@
s.receive(p);
fail("Received unexpected reply");
} catch (IOException expected) {
- checkConnectionOwnerUidUdp(s, false);
+ checkConnectionOwnerUidUdp(s, expectConnectionOwnerIsVisible);
}
}
} finally {
@@ -540,19 +633,38 @@
}
}
+ private void checkTrafficOnVpn(String destination) throws Exception {
+ final InetAddress address = InetAddress.getByName(destination);
+
+ if (address instanceof Inet6Address) {
+ checkUdpEcho(destination, "2001:db8:1:2::ffe");
+ checkTcpReflection(destination, "2001:db8:1:2::ffe");
+ checkPing(destination);
+ } else {
+ checkUdpEcho(destination, "192.0.2.2");
+ checkTcpReflection(destination, "192.0.2.2");
+ }
+
+ }
+
+ private void checkNoTrafficOnVpn(String destination) throws IOException {
+ checkUdpEcho(destination, null);
+ checkTcpReflection(destination, null);
+ }
+
private void checkTrafficOnVpn() throws Exception {
- checkUdpEcho("192.0.2.251", "192.0.2.2");
- checkUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
- checkPing("2001:db8:dead:beef::f00");
- checkTcpReflection("192.0.2.252", "192.0.2.2");
- checkTcpReflection("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
+ checkTrafficOnVpn("192.0.2.251");
+ checkTrafficOnVpn("2001:db8:dead:beef::f00");
}
private void checkNoTrafficOnVpn() throws Exception {
- checkUdpEcho("192.0.2.251", null);
- checkUdpEcho("2001:db8:dead:beef::f00", null);
- checkTcpReflection("192.0.2.252", null);
- checkTcpReflection("2001:db8:dead:beef::f00", null);
+ checkNoTrafficOnVpn("192.0.2.251");
+ checkNoTrafficOnVpn("2001:db8:dead:beef::f00");
+ }
+
+ private void checkTrafficBypassesVpn(String destination) throws Exception {
+ checkUdpEcho(destination, null, true /* expectVpnOwnedConnection */);
+ checkTcpReflection(destination, null);
}
private FileDescriptor openSocketFd(String host, int port, int timeoutMs) throws Exception {
@@ -702,13 +814,95 @@
setAndVerifyPrivateDns(initialMode);
}
+ private NetworkRequest makeVpnNetworkRequest() {
+ return new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .build();
+ }
+
+ private void expectUnderlyingNetworks(TestableNetworkCallback callback,
+ @Nullable List<Network> expectUnderlyingNetworks) {
+ callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> (Objects.equals(expectUnderlyingNetworks,
+ ((RecorderCallback.CallbackEntry.CapabilitiesChanged) entry)
+ .getCaps().getUnderlyingNetworks())));
+ }
+
+ @Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+ public void testChangeUnderlyingNetworks() throws Exception {
+ assumeTrue(supportedHardware());
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
+ final TestableNetworkCallback callback = new TestableNetworkCallback();
+ final boolean isWifiEnabled = mWifiManager.isWifiEnabled();
+ testAndCleanup(() -> {
+ // Ensure both of wifi and mobile data are connected.
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ assertTrue("Wifi is not connected", (wifiNetwork != null));
+ final Network cellNetwork = mCtsNetUtils.connectToCell();
+ assertTrue("Mobile data is not connected", (cellNetwork != null));
+ // Store current default network.
+ final Network defaultNetwork = mCM.getActiveNetwork();
+ // Start VPN and set empty array as its underlying networks.
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
+ new String[] {"0.0.0.0/0", "::/0"} /* routes */,
+ "" /* allowedApplications */, "" /* disallowedApplications */,
+ null /* proxyInfo */, new ArrayList<>() /* underlyingNetworks */,
+ false /* isAlwaysMetered */);
+ // Acquire the NETWORK_SETTINGS permission for getting the underlying networks.
+ runWithShellPermissionIdentity(() -> {
+ mCM.registerNetworkCallback(makeVpnNetworkRequest(), callback);
+ // Check that this VPN doesn't have any underlying networks.
+ expectUnderlyingNetworks(callback, new ArrayList<Network>());
+
+ // Update the underlying networks to null and the underlying networks should follow
+ // the system default network.
+ updateUnderlyingNetworks(null);
+ expectUnderlyingNetworks(callback, List.of(defaultNetwork));
+
+ // Update the underlying networks to mobile data.
+ updateUnderlyingNetworks(new ArrayList<>(List.of(cellNetwork)));
+ // Check the underlying networks of NetworkCapabilities which comes from
+ // onCapabilitiesChanged is mobile data.
+ expectUnderlyingNetworks(callback, List.of(cellNetwork));
+
+ // Update the underlying networks to wifi.
+ updateUnderlyingNetworks(new ArrayList<>(List.of(wifiNetwork)));
+ // Check the underlying networks of NetworkCapabilities which comes from
+ // onCapabilitiesChanged is wifi.
+ expectUnderlyingNetworks(callback, List.of(wifiNetwork));
+
+ // Update the underlying networks to wifi and mobile data.
+ updateUnderlyingNetworks(new ArrayList<>(List.of(wifiNetwork, cellNetwork)));
+ // Check the underlying networks of NetworkCapabilities which comes from
+ // onCapabilitiesChanged is wifi and mobile data.
+ expectUnderlyingNetworks(callback, List.of(wifiNetwork, cellNetwork));
+ }, NETWORK_SETTINGS);
+ }, () -> {
+ if (isWifiEnabled) {
+ mCtsNetUtils.ensureWifiConnected();
+ } else {
+ mCtsNetUtils.ensureWifiDisconnected(null);
+ }
+ }, () -> {
+ mCM.unregisterNetworkCallback(callback);
+ });
+ }
+
+ @Test
public void testDefault() throws Exception {
- if (!supportedHardware()) return;
- // If adb TCP port opened, this test may running by adb over network.
- // All of socket would be destroyed in this test. So this test don't
- // support adb over network, see b/119382723.
- if (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
- || SystemProperties.getInt("service.adb.tcp.port", -1) > -1) {
+ assumeTrue(supportedHardware());
+ if (!SdkLevel.isAtLeastS() && (
+ SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
+ || SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) {
+ // If adb TCP port opened, this test may running by adb over network.
+ // All of socket would be destroyed in this test. So this test don't
+ // support adb over network, see b/119382723.
+ // This is fixed in S, but still affects previous Android versions,
+ // and this test must be backwards compatible.
+ // TODO: Delete this code entirely when R is no longer supported.
Log.i(TAG, "adb is running over the network, so skip this test");
return;
}
@@ -762,6 +956,15 @@
maybeExpectVpnTransportInfo(vpnNetwork);
assertEquals(TYPE_VPN, mCM.getNetworkInfo(vpnNetwork).getType());
+ if (SdkLevel.isAtLeastT()) {
+ runWithShellPermissionIdentity(() -> {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
+ assertNotNull(nc);
+ assertNotNull(nc.getUnderlyingNetworks());
+ assertEquals(defaultNetwork, new ArrayList<>(nc.getUnderlyingNetworks()).get(0));
+ }, NETWORK_SETTINGS);
+ }
+
if (SdkLevel.isAtLeastS()) {
// Check that system default network callback has not seen any network changes, even
// though the app's default network changed. Also check that otherUidCallback saw no
@@ -781,8 +984,9 @@
receiver.unregisterQuietly();
}
+ @Test
public void testAppAllowed() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
@@ -801,23 +1005,29 @@
checkStrictModePrivateDns();
}
+ @Test
public void testAppDisallowed() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
- // If adb TCP port opened, this test may running by adb over TCP.
- // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
- // see b/119382723.
- // Note: The test don't support running adb over network for root device
- disallowedApps = disallowedApps + ",com.android.shell";
+ if (!SdkLevel.isAtLeastS()) {
+ // If adb TCP port opened, this test may running by adb over TCP.
+ // Add com.android.shell application into disallowedApps to exclude adb socket for VPN
+ // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root).
+ //
+ // This is fixed in S, but still affects previous Android versions,
+ // and this test must be backwards compatible.
+ // TODO: Delete this code entirely when R is no longer supported.
+ disallowedApps = disallowedApps + ",com.android.shell";
+ }
Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps);
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
- new String[] {"192.0.2.0/24", "2001:db8::/32"},
- "", disallowedApps, null, null /* underlyingNetworks */,
- false /* isAlwaysMetered */);
+ new String[] {"192.0.2.0/24", "2001:db8::/32"},
+ "", disallowedApps, null, null /* underlyingNetworks */,
+ false /* isAlwaysMetered */);
assertSocketStillOpen(localFd, TEST_HOST);
assertSocketStillOpen(remoteFd, TEST_HOST);
@@ -829,8 +1039,77 @@
assertFalse(nc.hasTransport(TRANSPORT_VPN));
}
+ @Test
+ public void testExcludedRoutes() throws Exception {
+ assumeTrue(supportedHardware());
+ assumeTrue(SdkLevel.isAtLeastT());
+
+ // Shell app must not be put in here or it would kill the ADB-over-network use case
+ String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
+ startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
+ new String[]{"0.0.0.0/0", "::/0"} /* routes */,
+ new String[]{"192.0.2.0/24", "2001:db8::/32"} /* excludedRoutes */,
+ allowedApps, "" /* disallowedApplications */, null /* proxyInfo */,
+ null /* underlyingNetworks */, false /* isAlwaysMetered */);
+
+ // Excluded routes should bypass VPN.
+ checkTrafficBypassesVpn("192.0.2.1");
+ checkTrafficBypassesVpn("2001:db8:dead:beef::f00");
+ // Other routes should go through VPN, since default routes are included.
+ checkTrafficOnVpn("198.51.100.1");
+ checkTrafficOnVpn("2002:db8::1");
+ }
+
+ @Test
+ public void testIncludedRoutes() throws Exception {
+ assumeTrue(supportedHardware());
+
+ // Shell app must not be put in here or it would kill the ADB-over-network use case
+ String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
+ startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
+ new String[]{"192.0.2.0/24", "2001:db8::/32"} /* routes */,
+ allowedApps, "" /* disallowedApplications */, null /* proxyInfo */,
+ null /* underlyingNetworks */, false /* isAlwaysMetered */);
+
+ // Included routes should go through VPN.
+ checkTrafficOnVpn("192.0.2.1");
+ checkTrafficOnVpn("2001:db8:dead:beef::f00");
+ // Other routes should bypass VPN, since default routes are not included.
+ checkTrafficBypassesVpn("198.51.100.1");
+ checkTrafficBypassesVpn("2002:db8::1");
+ }
+
+ @Test
+ public void testInterleavedRoutes() throws Exception {
+ assumeTrue(supportedHardware());
+ assumeTrue(SdkLevel.isAtLeastT());
+
+ // Shell app must not be put in here or it would kill the ADB-over-network use case
+ String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
+ startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
+ new String[]{"0.0.0.0/0", "192.0.2.0/32", "::/0", "2001:db8::/128"} /* routes */,
+ new String[]{"192.0.2.0/24", "2001:db8::/32"} /* excludedRoutes */,
+ allowedApps, "" /* disallowedApplications */, null /* proxyInfo */,
+ null /* underlyingNetworks */, false /* isAlwaysMetered */,
+ true /* addRoutesByIpPrefix */);
+
+ // Excluded routes should bypass VPN.
+ checkTrafficBypassesVpn("192.0.2.1");
+ checkTrafficBypassesVpn("2001:db8:dead:beef::f00");
+
+ // Included routes inside excluded routes should go through VPN, since the longest common
+ // prefix precedes.
+ checkTrafficOnVpn("192.0.2.0");
+ checkTrafficOnVpn("2001:db8::");
+
+ // Other routes should go through VPN, since default routes are included.
+ checkTrafficOnVpn("198.51.100.1");
+ checkTrafficOnVpn("2002:db8::1");
+ }
+
+ @Test
public void testGetConnectionOwnerUidSecurity() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
DatagramSocket s;
InetAddress address = InetAddress.getByName("localhost");
@@ -850,8 +1129,9 @@
}
}
+ @Test
public void testSetProxy() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
// Receiver for the proxy change broadcast.
BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
@@ -889,15 +1169,22 @@
assertDefaultProxy(initialProxy);
}
+ @Test
public void testSetProxyDisallowedApps() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
- // If adb TCP port opened, this test may running by adb over TCP.
- // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
- // see b/119382723.
- // Note: The test don't support running adb over network for root device
- String disallowedApps = mPackageName + ",com.android.shell";
+ String disallowedApps = mPackageName;
+ if (!SdkLevel.isAtLeastS()) {
+ // If adb TCP port opened, this test may running by adb over TCP.
+ // Add com.android.shell application into disallowedApps to exclude adb socket for VPN
+ // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root).
+ //
+ // This is fixed in S, but still affects previous Android versions,
+ // and this test must be backwards compatible.
+ // TODO: Delete this code entirely when R is no longer supported.
+ disallowedApps += ",com.android.shell";
+ }
ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"0.0.0.0/0", "::/0"}, "", disallowedApps,
@@ -908,8 +1195,9 @@
assertDefaultProxy(initialProxy);
}
+ @Test
public void testNoProxy() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
proxyBroadcastReceiver.register();
@@ -942,8 +1230,9 @@
assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork());
}
+ @Test
public void testBindToNetworkWithProxy() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
String allowedApps = mPackageName;
Network initialNetwork = mCM.getActiveNetwork();
ProxyInfo initialProxy = mCM.getDefaultProxy();
@@ -966,6 +1255,7 @@
assertDefaultProxy(initialProxy);
}
+ @Test
public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -986,8 +1276,18 @@
assertTrue(mCM.isActiveNetworkMetered());
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
+
+ if (SdkLevel.isAtLeastT()) {
+ runWithShellPermissionIdentity(() -> {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(mNetwork);
+ assertNotNull(nc);
+ assertNotNull(nc.getUnderlyingNetworks());
+ assertEquals(underlyingNetworks, new ArrayList<>(nc.getUnderlyingNetworks()));
+ }, NETWORK_SETTINGS);
+ }
}
+ @Test
public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -1016,6 +1316,7 @@
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
}
+ @Test
public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -1043,8 +1344,21 @@
assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered());
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
+
+ if (SdkLevel.isAtLeastT()) {
+ final Network vpnNetwork = mCM.getActiveNetwork();
+ assertNotEqual(underlyingNetwork, vpnNetwork);
+ runWithShellPermissionIdentity(() -> {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
+ assertNotNull(nc);
+ assertNotNull(nc.getUnderlyingNetworks());
+ final List<Network> underlying = nc.getUnderlyingNetworks();
+ assertEquals(underlyingNetwork, underlying.get(0));
+ }, NETWORK_SETTINGS);
+ }
}
+ @Test
public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -1071,6 +1385,7 @@
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
}
+ @Test
public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -1096,8 +1411,21 @@
assertTrue(mCM.isActiveNetworkMetered());
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
+
+ if (SdkLevel.isAtLeastT()) {
+ final Network vpnNetwork = mCM.getActiveNetwork();
+ assertNotEqual(underlyingNetwork, vpnNetwork);
+ runWithShellPermissionIdentity(() -> {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
+ assertNotNull(nc);
+ assertNotNull(nc.getUnderlyingNetworks());
+ final List<Network> underlying = nc.getUnderlyingNetworks();
+ assertEquals(underlyingNetwork, underlying.get(0));
+ }, NETWORK_SETTINGS);
+ }
}
+ @Test
public void testB141603906() throws Exception {
if (!supportedHardware()) {
return;
@@ -1146,7 +1474,7 @@
}
private void maybeExpectVpnTransportInfo(Network network) {
- if (!SdkLevel.isAtLeastS()) return;
+ assumeTrue(SdkLevel.isAtLeastS());
final NetworkCapabilities vpnNc = mCM.getNetworkCapabilities(network);
assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
final TransportInfo ti = vpnNc.getTransportInfo();
@@ -1176,7 +1504,7 @@
private boolean received;
public ProxyChangeBroadcastReceiver() {
- super(VpnTest.this.getInstrumentation().getContext(), Proxy.PROXY_CHANGE_ACTION);
+ super(getInstrumentation().getContext(), Proxy.PROXY_CHANGE_ACTION);
received = false;
}
@@ -1196,8 +1524,9 @@
* allowed list.
* See b/165774987.
*/
+ @Test
public void testDownloadWithDownloadManagerDisallowed() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
// Start a VPN with DownloadManager package in disallowed list.
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
@@ -1205,7 +1534,7 @@
"" /* allowedApps */, "com.android.providers.downloads", null /* proxyInfo */,
null /* underlyingNetworks */, false /* isAlwaysMetered */);
- final Context context = VpnTest.this.getInstrumentation().getContext();
+ final Context context = getInstrumentation().getContext();
final DownloadManager dm = context.getSystemService(DownloadManager.class);
final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
try {
diff --git a/tests/cts/hostside/app2/Android.bp b/tests/cts/hostside/app2/Android.bp
index b448459..4c9bccf 100644
--- a/tests/cts/hostside/app2/Android.bp
+++ b/tests/cts/hostside/app2/Android.bp
@@ -21,13 +21,14 @@
android_test_helper_app {
name: "CtsHostsideNetworkTestsApp2",
defaults: ["cts_support_defaults"],
- sdk_version: "current",
+ sdk_version: "test_current",
static_libs: ["CtsHostsideNetworkTestsAidl"],
srcs: ["src/**/*.java"],
// Tag this module as a cts test artifact
test_suites: [
"cts",
"general-tests",
+ "sts",
],
certificate: ":cts-net-app",
}
diff --git a/tests/cts/hostside/app2/AndroidManifest.xml b/tests/cts/hostside/app2/AndroidManifest.xml
index ad270b3..6c9b469 100644
--- a/tests/cts/hostside/app2/AndroidManifest.xml
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -16,40 +16,59 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.net.hostside.app2" >
+ package="com.android.cts.net.hostside.app2">
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!--
- This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
- them in a shared preferences which is then read by the test app. These broadcasts are
- handled by 2 listeners, one defined the manifest and another dynamically registered by
- a service.
+ This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
+ them in a shared preferences which is then read by the test app. These broadcasts are
+ handled by 2 listeners, one defined the manifest and another dynamically registered by
+ a service.
- The manifest-defined listener also handles ordered broadcasts used to share data with the
- test app.
+ The manifest-defined listener also handles ordered broadcasts used to share data with the
+ test app.
- This application also provides a service, RemoteSocketFactoryService, that the test app can
- use to open sockets to remote hosts as a different user ID.
+ This application also provides a service, RemoteSocketFactoryService, that the test app can
+ use to open sockets to remote hosts as a different user ID.
-->
- <application android:usesCleartextTraffic="true">
- <activity android:name=".MyActivity" android:exported="true"/>
- <service android:name=".MyService" android:exported="true"/>
- <service android:name=".MyForegroundService" android:exported="true"/>
- <service android:name=".RemoteSocketFactoryService" android:exported="true"/>
+ <application android:usesCleartextTraffic="true"
+ android:testOnly="true"
+ android:debuggable="true">
- <receiver android:name=".MyBroadcastReceiver" >
+ <activity android:name=".MyActivity"
+ android:exported="true"/>
+ <service android:name=".MyService"
+ android:exported="true"/>
+ <service android:name=".MyForegroundService"
+ android:exported="true"/>
+ <service android:name=".RemoteSocketFactoryService"
+ android:exported="true"/>
+
+ <receiver android:name=".MyBroadcastReceiver"
+ android:exported="true">
<intent-filter>
- <action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED" />
- <action android:name="com.android.cts.net.hostside.app2.action.GET_COUNTERS" />
- <action android:name="com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS" />
- <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK" />
- <action android:name="com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION" />
- <action android:name="com.android.cts.net.hostside.app2.action.SHOW_TOAST" />
+ <action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED"/>
+ <action android:name="com.android.cts.net.hostside.app2.action.GET_COUNTERS"/>
+ <action android:name="com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS"/>
+ <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK"/>
+ <action android:name="com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION"/>
+ <action android:name="com.android.cts.net.hostside.app2.action.SHOW_TOAST"/>
</intent-filter>
</receiver>
+ <service android:name=".MyJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
+ <!--
+ Adding this to make sure that receiving the broadcast is not restricted by
+ package visibility restrictions.
+ -->
+ <queries>
+ <package android:name="android" />
+ </queries>
+
</manifest>
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
index 351733e..2e79182 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -15,11 +15,13 @@
*/
package com.android.cts.net.hostside.app2;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -38,8 +40,13 @@
"com.android.cts.net.hostside.app2.action.RECEIVER_READY";
static final String ACTION_FINISH_ACTIVITY =
"com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
+ static final String ACTION_FINISH_JOB =
+ "com.android.cts.net.hostside.app2.action.FINISH_JOB";
static final String ACTION_SHOW_TOAST =
"com.android.cts.net.hostside.app2.action.SHOW_TOAST";
+ // Copied from com.android.server.net.NetworkPolicyManagerService class
+ static final String ACTION_SNOOZE_WARNING =
+ "com.android.server.net.action.SNOOZE_WARNING";
static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
static final String NOTIFICATION_TYPE_DELETE = "DELETE";
@@ -51,6 +58,11 @@
static final String TEST_PKG = "com.android.cts.net.hostside";
static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+ static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks";
+
+ static final int TYPE_COMPONENT_ACTIVTY = 0;
+ static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
+ static final int TYPE_COMPONENT_EXPEDITED_JOB = 2;
static int getUid(Context context) {
final String packageName = context.getPackageName();
@@ -61,11 +73,57 @@
}
}
- static void notifyNetworkStateObserver(Context context, Intent intent) {
+ private static boolean validateComponentState(Context context, int componentType,
+ INetworkStateObserver observer) throws RemoteException {
+ final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+ switch (componentType) {
+ case TYPE_COMPONENT_ACTIVTY: {
+ final int procState = activityManager.getUidProcessState(Process.myUid());
+ if (procState != ActivityManager.PROCESS_STATE_TOP) {
+ observer.onNetworkStateChecked(
+ INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE,
+ "Unexpected procstate: " + procState);
+ return false;
+ }
+ return true;
+ }
+ case TYPE_COMPONENT_FOREGROUND_SERVICE: {
+ final int procState = activityManager.getUidProcessState(Process.myUid());
+ if (procState != ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ observer.onNetworkStateChecked(
+ INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE,
+ "Unexpected procstate: " + procState);
+ return false;
+ }
+ return true;
+ }
+ case TYPE_COMPONENT_EXPEDITED_JOB: {
+ final int capabilities = activityManager.getUidProcessCapabilities(Process.myUid());
+ if ((capabilities & ActivityManager.PROCESS_CAPABILITY_NETWORK) == 0) {
+ observer.onNetworkStateChecked(
+ INetworkStateObserver.RESULT_ERROR_UNEXPECTED_CAPABILITIES,
+ "Unexpected capabilities: " + capabilities);
+ return false;
+ }
+ return true;
+ }
+ default: {
+ observer.onNetworkStateChecked(INetworkStateObserver.RESULT_ERROR_OTHER,
+ "Unknown component type: " + componentType);
+ return false;
+ }
+ }
+ }
+
+ static void notifyNetworkStateObserver(Context context, Intent intent, int componentType) {
if (intent == null) {
return;
}
final Bundle extras = intent.getExtras();
+ notifyNetworkStateObserver(context, extras, componentType);
+ }
+
+ static void notifyNetworkStateObserver(Context context, Bundle extras, int componentType) {
if (extras == null) {
return;
}
@@ -73,17 +131,17 @@
extras.getBinder(KEY_NETWORK_STATE_OBSERVER));
if (observer != null) {
try {
- if (!observer.isForeground()) {
- Log.e(TAG, "App didn't come to foreground");
- observer.onNetworkStateChecked(null);
+ final boolean skipValidation = extras.getBoolean(KEY_SKIP_VALIDATION_CHECKS);
+ if (!skipValidation && !validateComponentState(context, componentType, observer)) {
return;
}
} catch (RemoteException e) {
- Log.e(TAG, "Error occurred while reading the proc state: " + e);
+ Log.e(TAG, "Error occurred while informing the validation result: " + e);
}
AsyncTask.execute(() -> {
try {
observer.onNetworkStateChecked(
+ INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED,
MyBroadcastReceiver.checkNetworkStatus(context));
} catch (RemoteException e) {
Log.e(TAG, "Error occurred while notifying the observer: " + e);
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
index 286cc2f..9fdb9c9 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -18,6 +18,7 @@
import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
import static com.android.cts.net.hostside.app2.Common.TAG;
import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
+import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_ACTIVTY;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -42,7 +43,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "MyActivity.onCreate()");
- Common.notifyNetworkStateObserver(this, getIntent());
+ Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
finishCommandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
index aa54075..771b404 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -20,6 +20,7 @@
import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
import static com.android.cts.net.hostside.app2.Common.ACTION_SHOW_TOAST;
+import static com.android.cts.net.hostside.app2.Common.ACTION_SNOOZE_WARNING;
import static com.android.cts.net.hostside.app2.Common.MANIFEST_RECEIVER;
import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION;
import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_BUNDLE;
@@ -76,6 +77,9 @@
Log.d(TAG, "onReceive() for " + mName + ": " + intent);
final String action = intent.getAction();
switch (action) {
+ case ACTION_SNOOZE_WARNING:
+ increaseCounter(context, action);
+ break;
case ACTION_RESTRICT_BACKGROUND_CHANGED:
increaseCounter(context, action);
break;
@@ -201,7 +205,7 @@
Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType);
final Intent serviceIntent = new Intent(context, MyService.class);
final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent,
- notificationId);
+ PendingIntent.FLAG_MUTABLE);
final Bundle bundle = new Bundle();
bundle.putCharSequence("parcelable", "I am not");
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
index ff4ba65..b55761c 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
@@ -17,6 +17,7 @@
import static com.android.cts.net.hostside.app2.Common.TAG;
import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
+import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_FOREGROUND_SERVICE;
import android.R;
import android.app.Notification;
@@ -58,7 +59,7 @@
startForeground(42, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
.build());
- Common.notifyNetworkStateObserver(this, intent);
+ Common.notifyNetworkStateObserver(this, intent, TYPE_COMPONENT_FOREGROUND_SERVICE);
break;
case FLAG_STOP_FOREGROUND:
Log.d(TAG, "Stopping foreground");
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java
new file mode 100644
index 0000000..51c3157
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyJobService.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_JOB;
+import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.net.hostside.app2.Common.TYPE_COMPONENT_EXPEDITED_JOB;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+public class MyJobService extends JobService {
+
+ private BroadcastReceiver mFinishCommandReceiver = null;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.v(TAG, "MyJobService.onCreate()");
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Log.v(TAG, "MyJobService.onStartJob()");
+ Common.notifyNetworkStateObserver(this, params.getTransientExtras(),
+ TYPE_COMPONENT_EXPEDITED_JOB);
+ mFinishCommandReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.v(TAG, "Finishing MyJobService");
+ try {
+ jobFinished(params, /*wantsReschedule=*/ false);
+ } finally {
+ if (mFinishCommandReceiver != null) {
+ unregisterReceiver(mFinishCommandReceiver);
+ mFinishCommandReceiver = null;
+ }
+ }
+ }
+ };
+ registerReceiver(mFinishCommandReceiver, new IntentFilter(ACTION_FINISH_JOB));
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ // If this job is stopped before it had a chance to send network status via
+ // INetworkStateObserver, the test will fail. It could happen either due to test timing out
+ // or this app moving to a lower proc_state and losing network access.
+ Log.v(TAG, "MyJobService.onStopJob()");
+ if (mFinishCommandReceiver != null) {
+ unregisterReceiver(mFinishCommandReceiver);
+ mFinishCommandReceiver = null;
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.v(TAG, "MyJobService.onDestroy()");
+ }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
index 717ccb1..3b5e46f 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -18,12 +18,15 @@
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.net.hostside.app2.Common.ACTION_SNOOZE_WARNING;
import static com.android.cts.net.hostside.app2.Common.DYNAMIC_RECEIVER;
import static com.android.cts.net.hostside.app2.Common.TAG;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -65,6 +68,7 @@
context.registerReceiver(mReceiver, new IntentFilter(ACTION_RECEIVER_READY));
context.registerReceiver(mReceiver,
new IntentFilter(ACTION_RESTRICT_BACKGROUND_CHANGED));
+ context.registerReceiver(mReceiver, new IntentFilter(ACTION_SNOOZE_WARNING));
Log.d(TAG, "receiver registered");
}
@@ -154,6 +158,13 @@
mNetworkCallback = null;
}
}
+
+ @Override
+ public void scheduleJob(JobInfo jobInfo) {
+ final JobScheduler jobScheduler = getApplicationContext()
+ .getSystemService(JobScheduler.class);
+ jobScheduler.schedule(jobInfo);
+ }
};
@Override
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index 37420bf..cc07fd1 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -20,6 +20,7 @@
import com.android.ddmlib.Log;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.modules.utils.build.testing.DeviceSdkLevel;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.result.CollectingTestListener;
@@ -42,6 +43,7 @@
protected static final String TAG = "HostsideNetworkTests";
protected static final String TEST_PKG = "com.android.cts.net.hostside";
protected static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
+ protected static final String TEST_APK_NEXT = "CtsHostsideNetworkTestsAppNext.apk";
protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
protected static final String TEST_APP2_APK = "CtsHostsideNetworkTestsApp2.apk";
@@ -65,8 +67,12 @@
assertNotNull(mAbi);
assertNotNull(mCtsBuild);
+ DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(getDevice());
+ String testApk = deviceSdkLevel.isDeviceAtLeastT() ? TEST_APK_NEXT
+ : TEST_APK;
+
uninstallPackage(TEST_PKG, false);
- installPackage(TEST_APK);
+ installPackage(testApk);
}
@Override
@@ -80,7 +86,7 @@
DeviceNotAvailableException {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
assertNull(getDevice().installPackage(buildHelper.getTestFile(apk),
- false /* reinstall */, true /* grantPermissions */));
+ false /* reinstall */, true /* grantPermissions */, "-t"));
}
protected void uninstallPackage(String packageName, boolean shouldSucceed)
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index a629ccf..a95fc64 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -16,6 +16,9 @@
package com.android.cts.net;
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.SecurityTest;
+
import com.android.ddmlib.Log;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -36,6 +39,12 @@
uninstallPackage(TEST_APP2_PKG, true);
}
+ @SecurityTest
+ public void testDataWarningReceiver() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DataWarningReceiverTest",
+ "testSnoozeWarningNotReceived");
+ }
+
/**************************
* Data Saver Mode tests. *
**************************/
@@ -146,6 +155,7 @@
"testBackgroundNetworkAccess_disabled");
}
+ @FlakyTest(bugId=170180675)
public void testAppIdleMetered_whitelisted() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testBackgroundNetworkAccess_whitelisted");
@@ -176,6 +186,7 @@
"testBackgroundNetworkAccess_disabled");
}
+ @FlakyTest(bugId=170180675)
public void testAppIdleNonMetered_whitelisted() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testBackgroundNetworkAccess_whitelisted");
@@ -314,11 +325,23 @@
/**************************
* Restricted mode tests. *
**************************/
- public void testRestrictedMode_networkAccess() throws Exception {
+ public void testNetworkAccess_restrictedMode() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".RestrictedModeTest",
"testNetworkAccess");
}
+ /************************
+ * Expedited job tests. *
+ ************************/
+
+ public void testMeteredNetworkAccess_expeditedJob() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".ExpeditedJobMeteredTest");
+ }
+
+ public void testNonMeteredNetworkAccess_expeditedJob() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".ExpeditedJobNonMeteredTest");
+ }
+
/*******************
* Helper methods. *
*******************/
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index 49b5f9d..3821f87 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -33,6 +33,10 @@
uninstallPackage(TEST_APP2_PKG, true);
}
+ public void testChangeUnderlyingNetworks() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testChangeUnderlyingNetworks");
+ }
+
public void testDefault() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testDefault");
}
@@ -100,4 +104,16 @@
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest",
"testDownloadWithDownloadManagerDisallowed");
}
+
+ public void testExcludedRoutes() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testExcludedRoutes");
+ }
+
+ public void testIncludedRoutes() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testIncludedRoutes");
+ }
+
+ public void testInterleavedRoutes() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testInterleavedRoutes");
+ }
}
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index e28c33f..f66231d 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -18,7 +18,10 @@
java_defaults {
name: "CtsNetTestCasesDefaults",
- defaults: ["cts_defaults"],
+ defaults: [
+ "cts_defaults",
+ "framework-connectivity-test-defaults",
+ ],
// Include both the 32 and 64 bit versions
compile_multilib: "both",
@@ -46,6 +49,7 @@
"FrameworksNetCommonTests",
"core-tests-support",
"cts-net-utils",
+ "CtsNetTestsNonUpdatableLib",
"ctstestrunner-axt",
"junit",
"junit-params",
@@ -57,6 +61,7 @@
// uncomment when b/13249961 is fixed
// sdk_version: "current",
platform_apis: true,
+ required: ["ConnectivityChecker"],
}
// Networking CTS tests for development and release. These tests always target the platform SDK
@@ -65,7 +70,7 @@
// devices.
android_test {
name: "CtsNetTestCases",
- defaults: ["CtsNetTestCasesDefaults"],
+ defaults: ["CtsNetTestCasesDefaults", "NetworkStackNextEnableDefaults"],
// TODO: CTS should not depend on the entirety of the networkstack code.
static_libs: [
"NetworkStackApiCurrentLib",
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 78a01e2..d761c27 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -26,6 +26,8 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="{MODULE}.apk" />
</target_preparer>
+ <target_preparer class="com.android.testutils.ConnectivityCheckTargetPreparer">
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.cts" />
<option name="runtime-hint" value="9m4s" />
diff --git a/tests/cts/net/OWNERS b/tests/cts/net/OWNERS
index 432bd9b..df5569e 100644
--- a/tests/cts/net/OWNERS
+++ b/tests/cts/net/OWNERS
@@ -1,3 +1,5 @@
# Bug component: 31808
# Inherits parent owners
per-file src/android/net/cts/NetworkWatchlistTest.java=alanstokes@google.com
+
+# Bug component: 685852 = per-file *IpSec*
\ No newline at end of file
diff --git a/tests/cts/net/api23Test/AndroidManifest.xml b/tests/cts/net/api23Test/AndroidManifest.xml
index 4889660..69ee0dd 100644
--- a/tests/cts/net/api23Test/AndroidManifest.xml
+++ b/tests/cts/net/api23Test/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.net.cts.api23test">
+ package="android.net.cts.api23test">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -26,20 +26,20 @@
<uses-permission android:name="android.permission.INTERNET" />
<application android:usesCleartextTraffic="true">
- <uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.test.runner"/>
- <receiver android:name=".ConnectivityReceiver">
+ <receiver android:name=".ConnectivityReceiver"
+ android:exported="true">
<intent-filter>
- <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+ <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.net.cts.api23test"
- android:label="CTS tests of android.net">
+ android:targetPackage="android.net.cts.api23test"
+ android:label="CTS tests of android.net">
<meta-data android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
</instrumentation>
</manifest>
-
diff --git a/tests/cts/net/appForApi23/AndroidManifest.xml b/tests/cts/net/appForApi23/AndroidManifest.xml
index ed4cedb..158b9c4 100644
--- a/tests/cts/net/appForApi23/AndroidManifest.xml
+++ b/tests/cts/net/appForApi23/AndroidManifest.xml
@@ -16,32 +16,32 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.net.cts.appForApi23">
+ package="android.net.cts.appForApi23">
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
<application>
- <receiver android:name=".ConnectivityReceiver">
+ <receiver android:name=".ConnectivityReceiver"
+ android:exported="true">
<intent-filter>
- <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+ <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
<intent-filter>
- <action android:name="android.net.cts.appForApi23.getWifiConnectivityActionCount" />
+ <action android:name="android.net.cts.appForApi23.getWifiConnectivityActionCount"/>
</intent-filter>
</receiver>
<activity android:name=".ConnectivityListeningActivity"
- android:label="ConnectivityListeningActivity"
- android:exported="true">
+ android:label="ConnectivityListeningActivity"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
-
diff --git a/tests/cts/net/src/android/net/cts/AirplaneModeTest.java b/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
deleted file mode 100644
index 524e549..0000000
--- a/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
+++ /dev/null
@@ -1,86 +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.cts;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.lang.Thread;
-
-@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
-public class AirplaneModeTest extends AndroidTestCase {
- private static final String TAG = "AirplaneModeTest";
- private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
- private static final String FEATURE_WIFI = "android.hardware.wifi";
- private static final int TIMEOUT_MS = 10 * 1000;
- private boolean mHasFeature;
- private Context mContext;
- private ContentResolver resolver;
-
- public void setup() {
- mContext= getContext();
- resolver = mContext.getContentResolver();
- mHasFeature = (mContext.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH)
- || mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI));
- }
-
- public void testAirplaneMode() {
- setup();
- if (!mHasFeature) {
- Log.i(TAG, "The device doesn't support network bluetooth or wifi feature");
- return;
- }
-
- for (int testCount = 0; testCount < 2; testCount++) {
- if (!doOneTest()) {
- fail("Airplane mode failed to change in " + TIMEOUT_MS + "msec");
- return;
- }
- }
- }
-
- private boolean doOneTest() {
- boolean airplaneModeOn = isAirplaneModeOn();
- setAirplaneModeOn(!airplaneModeOn);
-
- try {
- Thread.sleep(TIMEOUT_MS);
- } catch (InterruptedException e) {
- Log.e(TAG, "Sleep time interrupted.", e);
- }
-
- if (airplaneModeOn == isAirplaneModeOn()) {
- return false;
- }
- return true;
- }
-
- private void setAirplaneModeOn(boolean enabling) {
- // Change the system setting for airplane mode
- Settings.Global.putInt(resolver, Settings.Global.AIRPLANE_MODE_ON, enabling ? 1 : 0);
- }
-
- private boolean isAirplaneModeOn() {
- // Read the system setting for airplane mode
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
- }
-}
diff --git a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
index 80951ca..6b2a1ee 100644
--- a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
@@ -41,11 +41,11 @@
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
+import androidx.test.filters.RequiresDevice;
import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.SkipPresubmit;
import org.junit.Before;
import org.junit.Rule;
@@ -94,7 +94,7 @@
// properly.
@Test
@AppModeFull(reason = "Cannot get CHANGE_NETWORK_STATE to request wifi/cell in instant mode")
- @SkipPresubmit(reason = "Virtual hardware does not support wifi battery stats")
+ @RequiresDevice // Virtual hardware does not support wifi battery stats
public void testReportNetworkInterfaceForTransports() throws Exception {
try {
// Simulate the device being unplugged from charging.
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index 9f98e3f..a378aa7 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -40,7 +40,6 @@
import android.net.cts.util.CtsNetUtils
import android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL
import android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL
-import android.net.wifi.WifiManager
import android.os.Build
import android.platform.test.annotations.AppModeFull
import android.provider.DeviceConfig
@@ -77,7 +76,7 @@
private const val LOCALHOST_HOSTNAME = "localhost"
// Re-connecting to the AP, obtaining an IP address, revalidating can take a long time
-private const val WIFI_CONNECT_TIMEOUT_MS = 120_000L
+private const val WIFI_CONNECT_TIMEOUT_MS = 40_000L
private const val TEST_TIMEOUT_MS = 10_000L
private const val TAG = "CaptivePortalTest"
@@ -94,7 +93,6 @@
@RunWith(AndroidJUnit4::class)
class CaptivePortalTest {
private val context: android.content.Context by lazy { getInstrumentation().context }
- private val wm by lazy { context.getSystemService(WifiManager::class.java) }
private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
private val pm by lazy { context.packageManager }
private val utils by lazy { CtsNetUtils(context) }
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index 721ad82..232114e 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -40,6 +40,7 @@
import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.testutils.Cleanup.testAndCleanup;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -84,7 +85,6 @@
import com.android.net.module.util.ArrayTrackRecord;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
-import com.android.testutils.SkipPresubmit;
import org.junit.After;
import org.junit.Before;
@@ -206,7 +206,6 @@
cb.assertNoCallback();
}
- @SkipPresubmit(reason = "Flaky: b/159718782; add to presubmit after fixing")
@Test
public void testRegisterCallbackWithCarrierPrivileges() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
@@ -224,16 +223,16 @@
final TestNetworkCallback testNetworkCallback = new TestNetworkCallback();
- try {
+ testAndCleanup(() -> {
doBroadcastCarrierConfigsAndVerifyOnConnectivityReportAvailable(
subId, carrierConfigReceiver, testNetworkCallback);
- } finally {
+ }, () -> {
runWithShellPermissionIdentity(
() -> mCarrierConfigManager.overrideConfig(subId, null),
android.Manifest.permission.MODIFY_PHONE_STATE);
mConnectivityManager.unregisterNetworkCallback(testNetworkCallback);
mContext.unregisterReceiver(carrierConfigReceiver);
- }
+ });
}
private String getCertHashForThisPackage() throws Exception {
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 65dffbd..80c2db4 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -75,6 +75,7 @@
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
+import static com.android.testutils.Cleanup.testAndCleanup;
import static com.android.testutils.MiscAsserts.assertThrows;
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
import static com.android.testutils.TestPermissionUtil.runAsShell;
@@ -119,10 +120,8 @@
import android.net.NetworkInfo.State;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
-import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkUtils;
import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.SocketKeepalive;
@@ -156,6 +155,7 @@
import android.util.Range;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.RequiresDevice;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
@@ -169,7 +169,6 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRuleKt;
import com.android.testutils.RecorderCallback.CallbackEntry;
-import com.android.testutils.SkipPresubmit;
import com.android.testutils.TestHttpServer;
import com.android.testutils.TestNetworkTracker;
import com.android.testutils.TestableNetworkCallback;
@@ -238,6 +237,7 @@
private static final int MIN_KEEPALIVE_INTERVAL = 10;
private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
+ private static final int LISTEN_ACTIVITY_TIMEOUT_MS = 5_000;
private static final int NO_CALLBACK_TIMEOUT_MS = 100;
private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
@@ -260,6 +260,7 @@
"config_allowedUnprivilegedKeepalivePerUid";
private static final String KEEPALIVE_RESERVED_PER_SLOT_RES_NAME =
"config_reservedPrivilegedKeepaliveSlots";
+ private static final String TEST_RESTRICTED_NW_IFACE_NAME = "test-restricted-nw";
private static final LinkAddress TEST_LINKADDR = new LinkAddress(
InetAddresses.parseNumericAddress("2001:db8::8"), 64);
@@ -279,6 +280,7 @@
private ConnectivityManagerShim mCmShim;
private WifiManager mWifiManager;
private PackageManager mPackageManager;
+ private TelephonyManager mTm;
private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
private UiAutomation mUiAutomation;
private CtsNetUtils mCtsNetUtils;
@@ -298,6 +300,7 @@
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mPackageManager = mContext.getPackageManager();
mCtsNetUtils = new CtsNetUtils(mContext);
+ mTm = mContext.getSystemService(TelephonyManager.class);
if (DevSdkIgnoreRuleKt.isDevSdkInRange(null /* minExclusive */,
Build.VERSION_CODES.R /* maxInclusive */)) {
@@ -556,7 +559,7 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Virtual devices use a single internet connection for all networks")
+ @RequiresDevice // Virtual devices use a single internet connection for all networks
public void testOpenConnection() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
@@ -711,6 +714,12 @@
.build();
}
+ private boolean hasPrivateDnsValidated(CallbackEntry entry, Network networkForPrivateDns) {
+ if (!networkForPrivateDns.equals(entry.getNetwork())) return false;
+ final NetworkCapabilities nc = ((CallbackEntry.CapabilitiesChanged) entry).getCaps();
+ return !nc.isPrivateDnsBroken() && nc.hasCapability(NET_CAPABILITY_VALIDATED);
+ }
+
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testIsPrivateDnsBroken() throws InterruptedException {
@@ -724,8 +733,7 @@
mCtsNetUtils.setPrivateDnsStrictMode(goodPrivateDnsServer);
final Network networkForPrivateDns = mCtsNetUtils.ensureWifiConnected();
cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
- .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
+ entry -> hasPrivateDnsValidated(entry, networkForPrivateDns));
// Verifying the broken private DNS sever
mCtsNetUtils.setPrivateDnsStrictMode(invalidPrivateDnsServer);
@@ -1082,7 +1090,8 @@
}
private Network waitForActiveNetworkMetered(final int targetTransportType,
- final boolean requestedMeteredness, final boolean useSystemDefault)
+ final boolean requestedMeteredness, final boolean waitForValidation,
+ final boolean useSystemDefault)
throws Exception {
final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
final NetworkCallback networkCallback = new NetworkCallback() {
@@ -1091,7 +1100,8 @@
if (!nc.hasTransport(targetTransportType)) return;
final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
- if (metered == requestedMeteredness) {
+ final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
+ if (metered == requestedMeteredness && (!waitForValidation || validated)) {
networkFuture.complete(network);
}
}
@@ -1120,11 +1130,13 @@
}
}
- private Network setWifiMeteredStatusAndWait(String ssid, boolean isMetered) throws Exception {
+ private Network setWifiMeteredStatusAndWait(String ssid, boolean isMetered,
+ boolean waitForValidation) throws Exception {
setWifiMeteredStatus(ssid, Boolean.toString(isMetered) /* metered */);
mCtsNetUtils.ensureWifiConnected();
return waitForActiveNetworkMetered(TRANSPORT_WIFI,
isMetered /* requestedMeteredness */,
+ waitForValidation,
true /* useSystemDefault */);
}
@@ -1176,6 +1188,7 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
public void testGetMultipathPreference() throws Exception {
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
final ContentResolver resolver = mContext.getContentResolver();
mCtsNetUtils.ensureWifiConnected();
final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
@@ -1187,9 +1200,10 @@
int newMeteredPreference = findNextPrefValue(resolver);
Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
Integer.toString(newMeteredPreference));
- // Wifi meterness changes from unmetered to metered will disconnect and reconnect since
- // R.
- final Network network = setWifiMeteredStatusAndWait(ssid, true);
+ // Wifi meteredness changes from unmetered to metered will disconnect and reconnect
+ // since R.
+ final Network network = setWifiMeteredStatusAndWait(ssid, true /* isMetered */,
+ false /* waitForValidation */);
assertEquals(ssid, unquoteSSID(mWifiManager.getConnectionInfo().getSSID()));
assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
NET_CAPABILITY_NOT_METERED), false);
@@ -1206,7 +1220,7 @@
oldMeteredPreference, newMeteredPreference);
// No disconnect from unmetered to metered.
- setWifiMeteredStatusAndWait(ssid, false);
+ setWifiMeteredStatusAndWait(ssid, false /* isMetered */, false /* waitForValidation */);
assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
NET_CAPABILITY_NOT_METERED), true);
assertMultipathPreferenceIsEventually(network, newMeteredPreference,
@@ -1411,7 +1425,7 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
+ @RequiresDevice // Keepalive is not supported on virtual hardware
public void testCreateTcpKeepalive() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -1618,7 +1632,7 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
+ @RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveLimitWifi() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -1668,7 +1682,7 @@
*/
@AppModeFull(reason = "Cannot request network in instant app mode")
@Test
- @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
+ @RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveLimitTelephony() throws Exception {
if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device"
@@ -1714,7 +1728,7 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
+ @RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveUnprivileged() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -1748,6 +1762,40 @@
greater >= lesser);
}
+ private void verifyBindSocketToRestrictedNetworkDisallowed() throws Exception {
+ final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
+ final NetworkRequest testRequest = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
+ .build();
+ runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS,
+ // CONNECTIVITY_INTERNAL is for requesting restricted network because shell does not
+ // have CONNECTIVITY_USE_RESTRICTED_NETWORKS on R.
+ CONNECTIVITY_INTERNAL);
+
+ // Create a restricted network and ensure this package cannot bind to that network either.
+ final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
+ final Network network = agent.getNetwork();
+
+ try (Socket socket = new Socket()) {
+ // Verify that the network is restricted.
+ testNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> network.equals(entry.getNetwork())
+ && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ // CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
+ // does not allow to bind socket to restricted network.
+ assertThrows(IOException.class, () -> network.bindSocket(socket));
+ } finally {
+ agent.unregister();
+ }
+ }
+
/**
* Verifies that apps are not allowed to access restricted networks even if they declare the
* CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests.
@@ -1755,6 +1803,7 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testRestrictedNetworkPermission() throws Exception {
// Ensure that CONNECTIVITY_USE_RESTRICTED_NETWORKS isn't granted to this package.
final PackageInfo app = mPackageManager.getPackageInfo(mContext.getPackageName(),
@@ -1764,23 +1813,33 @@
assertTrue(index >= 0);
assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED);
- // Ensure that NetworkUtils.queryUserAccess always returns false since this package should
- // not have netd system permission to call this function.
- final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
- assertFalse(NetworkUtils.queryUserAccess(Binder.getCallingUid(), wifiNetwork.netId));
+ if (mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
+ // Expect binding to the wifi network to succeed.
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ try (Socket socket = new Socket()) {
+ wifiNetwork.bindSocket(socket);
+ }
+ }
// Ensure that this package cannot bind to any restricted network that's currently
// connected.
Network[] networks = mCm.getAllNetworks();
for (Network network : networks) {
- NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
- if (nc != null && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
- try {
- network.bindSocket(new Socket());
- fail("Bind to restricted network " + network + " unexpectedly succeeded");
- } catch (IOException expected) {}
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+ if (nc == null) {
+ continue;
+ }
+
+ try (Socket socket = new Socket()) {
+ if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+ network.bindSocket(socket); // binding should succeed
+ } else {
+ assertThrows(IOException.class, () -> network.bindSocket(socket));
+ }
}
}
+
+ verifyBindSocketToRestrictedNetworkDisallowed();
}
/**
@@ -2321,9 +2380,11 @@
final String ssid = unquoteSSID(wifiNetworkCapabilities.getSsid());
final boolean oldMeteredValue = wifiNetworkCapabilities.isMetered();
- try {
- // This network will be used for unmetered.
- setWifiMeteredStatusAndWait(ssid, false /* isMetered */);
+ testAndCleanup(() -> {
+ // This network will be used for unmetered. Wait for it to be validated because
+ // OEM_NETWORK_PREFERENCE_TEST only prefers NOT_METERED&VALIDATED to a network with
+ // TRANSPORT_TEST, like OEM_NETWORK_PREFERENCE_OEM_PAID.
+ setWifiMeteredStatusAndWait(ssid, false /* isMetered */, true /* waitForValidation */);
setOemNetworkPreferenceForMyPackage(OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST);
registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
@@ -2332,9 +2393,10 @@
waitForAvailable(defaultCallback, wifiNetwork);
waitForAvailable(systemDefaultCallback, wifiNetwork);
- // Validate when setting unmetered to metered, unmetered is lost and replaced by the
- // network with the TEST transport.
- setWifiMeteredStatusAndWait(ssid, true /* isMetered */);
+ // Validate that when setting unmetered to metered, unmetered is lost and replaced by
+ // the network with the TEST transport. Also wait for validation here, in case there
+ // is a bug that's only visible when the network is validated.
+ setWifiMeteredStatusAndWait(ssid, true /* isMetered */, true /* waitForValidation */);
defaultCallback.expectCallback(CallbackEntry.LOST, wifiNetwork,
NETWORK_CALLBACK_TIMEOUT_MS);
waitForAvailable(defaultCallback, tnt.getNetwork());
@@ -2343,18 +2405,18 @@
// callback in any case therefore confirm its receipt before continuing to assure the
// system is in the expected state.
waitForAvailable(systemDefaultCallback, TRANSPORT_WIFI);
- } finally {
+ }, /* cleanup */ () -> {
// Validate that removing the test network will fallback to the default network.
runWithShellPermissionIdentity(tnt::teardown);
defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
NETWORK_CALLBACK_TIMEOUT_MS);
waitForAvailable(defaultCallback);
-
- setWifiMeteredStatusAndWait(ssid, oldMeteredValue);
-
- // Cleanup any prior test state from setOemNetworkPreference
- clearOemNetworkPreference();
- }
+ }, /* cleanup */ () -> {
+ setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
+ }, /* cleanup */ () -> {
+ // Cleanup any prior test state from setOemNetworkPreference
+ clearOemNetworkPreference();
+ });
}
/**
@@ -2375,29 +2437,30 @@
final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
- try {
+ testAndCleanup(() -> {
setOemNetworkPreferenceForMyPackage(
OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
waitForAvailable(defaultCallback, tnt.getNetwork());
waitForAvailable(systemDefaultCallback, wifiNetwork);
- } finally {
- runWithShellPermissionIdentity(tnt::teardown);
- defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
- NETWORK_CALLBACK_TIMEOUT_MS);
+ }, /* cleanup */ () -> {
+ runWithShellPermissionIdentity(tnt::teardown);
+ defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
+ NETWORK_CALLBACK_TIMEOUT_MS);
- // This network preference should only ever use the test network therefore available
- // should not trigger when the test network goes down (e.g. switch to cellular).
- defaultCallback.assertNoCallback();
- // The system default should still be connected to Wi-fi
- assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
+ // This network preference should only ever use the test network therefore available
+ // should not trigger when the test network goes down (e.g. switch to cellular).
+ defaultCallback.assertNoCallback();
+ // The system default should still be connected to Wi-fi
+ assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
+ }, /* cleanup */ () -> {
+ // Cleanup any prior test state from setOemNetworkPreference
+ clearOemNetworkPreference();
- // Cleanup any prior test state from setOemNetworkPreference
- clearOemNetworkPreference();
-
- // The default (non-test) network should be available as the network pref was cleared.
- waitForAvailable(defaultCallback);
- }
+ // The default (non-test) network should be available as the network pref was
+ // cleared.
+ waitForAvailable(defaultCallback);
+ });
}
private void registerTestOemNetworkPreferenceCallbacks(
@@ -2742,6 +2805,49 @@
System.currentTimeMillis() + WIFI_CONNECT_TIMEOUT_MS);
}
+ @AppModeFull(reason = "Need WiFi support to test the default active network")
+ @Test
+ public void testDefaultNetworkActiveListener() throws Exception {
+ final boolean supportWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI);
+ final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
+ assumeTrue("testDefaultNetworkActiveListener cannot execute"
+ + " unless device supports WiFi or telephony", (supportWifi || supportTelephony));
+
+ if (supportWifi) {
+ mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
+ } else {
+ mCtsNetUtils.disconnectFromCell();
+ }
+
+ final CompletableFuture<Boolean> future = new CompletableFuture<>();
+ final ConnectivityManager.OnNetworkActiveListener listener = () -> future.complete(true);
+ mCm.addDefaultNetworkActiveListener(listener);
+ testAndCleanup(() -> {
+ // New default network connected will trigger a network activity notification.
+ if (supportWifi) {
+ mCtsNetUtils.ensureWifiConnected();
+ } else {
+ mCtsNetUtils.connectToCell();
+ }
+ assertTrue(future.get(LISTEN_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }, () -> {
+ mCm.removeDefaultNetworkActiveListener(listener);
+ });
+ }
+
+ /**
+ * The networks used in this test are real networks and as such they can see seemingly random
+ * updates of their capabilities or link properties as conditions change, e.g. the network
+ * loses validation or IPv4 shows up. Many tests should simply treat these callbacks as
+ * spurious.
+ */
+ private void assertNoCallbackExceptCapOrLpChange(
+ @NonNull final TestableNetworkCallback cb) {
+ cb.assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS,
+ c -> !(c instanceof CallbackEntry.CapabilitiesChanged
+ || c instanceof CallbackEntry.LinkPropertiesChanged));
+ }
+
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
public void testMobileDataPreferredUids() throws Exception {
@@ -2774,8 +2880,7 @@
// CtsNetTestCases uid is not listed in MOBILE_DATA_PREFERRED_UIDS setting, so the
// per-app default network should be same as system default network.
waitForAvailable(systemDefaultCb, wifiNetwork);
- defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> wifiNetwork.equals(entry.getNetwork()));
+ waitForAvailable(defaultTrackingCb, wifiNetwork);
// Active network for CtsNetTestCases uid should be wifi now.
assertEquals(wifiNetwork, mCm.getActiveNetwork());
@@ -2785,10 +2890,10 @@
newMobileDataPreferredUids.add(uid);
ConnectivitySettingsManager.setMobileDataPreferredUids(
mContext, newMobileDataPreferredUids);
- defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> cellNetwork.equals(entry.getNetwork()));
- // System default network doesn't change.
- systemDefaultCb.assertNoCallback();
+ waitForAvailable(defaultTrackingCb, cellNetwork);
+ // No change for system default network. Expect no callback except CapabilitiesChanged
+ // or LinkPropertiesChanged which may be triggered randomly from wifi network.
+ assertNoCallbackExceptCapOrLpChange(systemDefaultCb);
// Active network for CtsNetTestCases uid should change to cell, too.
assertEquals(cellNetwork, mCm.getActiveNetwork());
@@ -2797,10 +2902,10 @@
newMobileDataPreferredUids.remove(uid);
ConnectivitySettingsManager.setMobileDataPreferredUids(
mContext, newMobileDataPreferredUids);
- defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> wifiNetwork.equals(entry.getNetwork()));
- // System default network still doesn't change.
- systemDefaultCb.assertNoCallback();
+ waitForAvailable(defaultTrackingCb, wifiNetwork);
+ // No change for system default network. Expect no callback except CapabilitiesChanged
+ // or LinkPropertiesChanged which may be triggered randomly from wifi network.
+ assertNoCallbackExceptCapOrLpChange(systemDefaultCb);
// Active network for CtsNetTestCases uid should change back to wifi.
assertEquals(wifiNetwork, mCm.getActiveNetwork());
} finally {
@@ -2832,6 +2937,24 @@
}
}
+ private static NetworkAgent createRestrictedNetworkAgent(final Context context) {
+ // Create test network agent with restricted network.
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
+ .build();
+ final NetworkAgent agent = new NetworkAgent(context, Looper.getMainLooper(), TAG, nc,
+ new LinkProperties(), 10 /* score */, new NetworkAgentConfig.Builder().build(),
+ new NetworkProvider(context, Looper.getMainLooper(), TAG)) {};
+ runWithShellPermissionIdentity(() -> agent.register(),
+ android.Manifest.permission.MANAGE_TEST_NETWORKS);
+ agent.markConnected();
+
+ return agent;
+ }
+
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
@Test
public void testUidsAllowedOnRestrictedNetworks() throws Exception {
@@ -2852,42 +2975,27 @@
ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
- final Handler h = new Handler(Looper.getMainLooper());
final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
- registerBestMatchingNetworkCallback(new NetworkRequest.Builder().clearCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), testNetworkCb, h);
-
- // Create test network agent with restricted network.
- final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
.build();
- final NetworkScore score = new NetworkScore.Builder()
- .setExiting(false)
- .setTransportPrimary(false)
- .setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_FOR_HANDOVER)
- .build();
- final NetworkAgent agent = new NetworkAgent(mContext, Looper.getMainLooper(),
- TAG, nc, new LinkProperties(), score, new NetworkAgentConfig.Builder().build(),
- new NetworkProvider(mContext, Looper.getMainLooper(), TAG)) {};
- runWithShellPermissionIdentity(() -> agent.register(),
- android.Manifest.permission.MANAGE_TEST_NETWORKS);
- agent.markConnected();
+ runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
final Network network = agent.getNetwork();
try (Socket socket = new Socket()) {
- testNetworkCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> network.equals(entry.getNetwork()));
// Verify that the network is restricted.
- final NetworkCapabilities testNetworkNc = mCm.getNetworkCapabilities(network);
- assertNotNull(testNetworkNc);
- assertFalse(testNetworkNc.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
+ testNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> network.equals(entry.getNetwork())
+ && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
// CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
// does not allow to bind socket to restricted network.
assertThrows(IOException.class, () -> network.bindSocket(socket));
diff --git a/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt b/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
new file mode 100644
index 0000000..555dd87
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts
+
+import android.net.DhcpOption
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.SC_V2
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.runner.RunWith
+import org.junit.Test
+
+@SmallTest
+@IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+@RunWith(DevSdkIgnoreRunner::class)
+class DhcpOptionTest {
+ private val DHCP_OPTION_TYPE: Byte = 2
+ private val DHCP_OPTION_VALUE = byteArrayOf(0, 1, 2, 4, 8, 16)
+
+ @Test
+ fun testConstructor() {
+ val dhcpOption = DhcpOption(DHCP_OPTION_TYPE, DHCP_OPTION_VALUE)
+ assertEquals(DHCP_OPTION_TYPE, dhcpOption.type)
+ assertArrayEquals(DHCP_OPTION_VALUE, dhcpOption.value)
+ }
+
+ @Test
+ fun testConstructorWithNullValue() {
+ val dhcpOption = DhcpOption(DHCP_OPTION_TYPE, null)
+ assertEquals(DHCP_OPTION_TYPE, dhcpOption.type)
+ assertNull(dhcpOption.value)
+ }
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index 4d95fbe..c6fc38f 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -22,17 +22,24 @@
import static android.net.DnsResolver.TYPE_A;
import static android.net.DnsResolver.TYPE_AAAA;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
import static android.system.OsConstants.ETIMEDOUT;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
+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 android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
import android.net.DnsResolver;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
@@ -42,14 +49,23 @@
import android.os.Handler;
import android.os.Looper;
import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
import android.system.ErrnoException;
-import android.test.AndroidTestCase;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.net.module.util.DnsPacket;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.SkipPresubmit;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -60,7 +76,11 @@
import java.util.concurrent.TimeUnit;
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
-public class DnsResolverTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class DnsResolverTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
private static final String TAG = "DnsResolverTest";
private static final char[] HEX_CHARS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
@@ -90,6 +110,7 @@
static final int QUERY_TIMES = 10;
static final int NXDOMAIN = 3;
+ private Context mContext;
private ContentResolver mCR;
private ConnectivityManager mCM;
private PackageManager mPackageManager;
@@ -98,26 +119,27 @@
private Executor mExecutorInline;
private DnsResolver mDns;
- private String mOldMode;
- private String mOldDnsSpecifier;
+ private TestNetworkCallback mWifiRequestCallback = null;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mCM = mContext.getSystemService(ConnectivityManager.class);
mDns = DnsResolver.getInstance();
mExecutor = new Handler(Looper.getMainLooper())::post;
mExecutorInline = (Runnable r) -> r.run();
- mCR = getContext().getContentResolver();
- mCtsNetUtils = new CtsNetUtils(getContext());
+ mCR = mContext.getContentResolver();
+ mCtsNetUtils = new CtsNetUtils(mContext);
mCtsNetUtils.storePrivateDnsSetting();
mPackageManager = mContext.getPackageManager();
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
mCtsNetUtils.restorePrivateDnsSetting();
- super.tearDown();
+ if (mWifiRequestCallback != null) {
+ mCM.unregisterNetworkCallback(mWifiRequestCallback);
+ }
}
private static String byteArrayToHexString(byte[] bytes) {
@@ -132,6 +154,14 @@
private Network[] getTestableNetworks() {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ // File a NetworkRequest for Wi-Fi, so it connects even if a higher-scoring
+ // network, such as Ethernet, is already connected.
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ mWifiRequestCallback = new TestNetworkCallback();
+ mCM.requestNetwork(request, mWifiRequestCallback);
mCtsNetUtils.ensureWifiConnected();
}
final ArrayList<Network> testableNetworks = new ArrayList<Network>();
@@ -285,42 +315,52 @@
}
}
+ @Test
public void testRawQuery() throws Exception {
doTestRawQuery(mExecutor);
}
+ @Test
public void testRawQueryInline() throws Exception {
doTestRawQuery(mExecutorInline);
}
+ @Test
public void testRawQueryBlob() throws Exception {
doTestRawQueryBlob(mExecutor);
}
+ @Test
public void testRawQueryBlobInline() throws Exception {
doTestRawQueryBlob(mExecutorInline);
}
+ @Test
public void testRawQueryRoot() throws Exception {
doTestRawQueryRoot(mExecutor);
}
+ @Test
public void testRawQueryRootInline() throws Exception {
doTestRawQueryRoot(mExecutorInline);
}
+ @Test
public void testRawQueryNXDomain() throws Exception {
doTestRawQueryNXDomain(mExecutor);
}
+ @Test
public void testRawQueryNXDomainInline() throws Exception {
doTestRawQueryNXDomain(mExecutorInline);
}
+ @Test
public void testRawQueryNXDomainWithPrivateDns() throws Exception {
doTestRawQueryNXDomainWithPrivateDns(mExecutor);
}
+ @Test
public void testRawQueryNXDomainInlineWithPrivateDns() throws Exception {
doTestRawQueryNXDomainWithPrivateDns(mExecutorInline);
}
@@ -423,6 +463,7 @@
}
}
+ @Test
public void testRawQueryCancel() throws InterruptedException {
final String msg = "Test cancel RawQuery " + TEST_DOMAIN;
// Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
@@ -452,6 +493,7 @@
}
}
+ @Test
public void testRawQueryBlobCancel() throws InterruptedException {
final String msg = "Test cancel RawQuery blob " + byteArrayToHexString(TEST_BLOB);
// Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
@@ -480,6 +522,7 @@
}
}
+ @Test
public void testCancelBeforeQuery() throws InterruptedException {
final String msg = "Test cancelled RawQuery " + TEST_DOMAIN;
for (Network network : getTestableNetworks()) {
@@ -565,34 +608,42 @@
}
}
+ @Test
public void testQueryForInetAddress() throws Exception {
doTestQueryForInetAddress(mExecutor);
}
+ @Test
public void testQueryForInetAddressInline() throws Exception {
doTestQueryForInetAddress(mExecutorInline);
}
+ @Test
public void testQueryForInetAddressIpv4() throws Exception {
doTestQueryForInetAddressIpv4(mExecutor);
}
+ @Test
public void testQueryForInetAddressIpv4Inline() throws Exception {
doTestQueryForInetAddressIpv4(mExecutorInline);
}
+ @Test
public void testQueryForInetAddressIpv6() throws Exception {
doTestQueryForInetAddressIpv6(mExecutor);
}
+ @Test
public void testQueryForInetAddressIpv6Inline() throws Exception {
doTestQueryForInetAddressIpv6(mExecutorInline);
}
+ @Test
public void testContinuousQueries() throws Exception {
doTestContinuousQueries(mExecutor);
}
+ @Test
@SkipPresubmit(reason = "Flaky: b/159762682; add to presubmit after fixing")
public void testContinuousQueriesInline() throws Exception {
doTestContinuousQueries(mExecutorInline);
@@ -612,6 +663,7 @@
}
}
+ @Test
public void testQueryCancelForInetAddress() throws InterruptedException {
final String msg = "Test cancel query for InetAddress " + TEST_DOMAIN;
// Start a DNS query and the cancel it immediately. Use VerifyCancelInetAddressCallback to
@@ -673,6 +725,7 @@
}
}
+ @Test
public void testPrivateDnsBypass() throws InterruptedException {
final Network[] testNetworks = getTestableNetworks();
@@ -760,4 +813,19 @@
}
}
}
+
+ /** Verifies that DnsResolver.DnsException can be subclassed and its constructor re-used. */
+ @Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+ public void testDnsExceptionConstructor() throws InterruptedException {
+ class TestDnsException extends DnsResolver.DnsException {
+ TestDnsException(int code, @Nullable Throwable cause) {
+ super(code, cause);
+ }
+ }
+ try {
+ throw new TestDnsException(DnsResolver.ERROR_SYSTEM, null);
+ } catch (DnsResolver.DnsException e) {
+ assertEquals(DnsResolver.ERROR_SYSTEM, e.code);
+ }
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/DnsTest.java b/tests/cts/net/src/android/net/cts/DnsTest.java
index fde27e9..fb63a19 100644
--- a/tests/cts/net/src/android/net/cts/DnsTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsTest.java
@@ -16,7 +16,6 @@
package android.net.cts;
-import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -27,7 +26,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
-import com.android.testutils.SkipPresubmit;
+import androidx.test.filters.RequiresDevice;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -70,7 +69,7 @@
* Perf - measure size of first and second tier caches and their effect
* Assert requires network permission
*/
- @SkipPresubmit(reason = "IPv6 support may be missing on presubmit virtual hardware")
+ @RequiresDevice // IPv6 support may be missing on presubmit virtual hardware
public void testDnsWorks() throws Exception {
ensureIpv6Connectivity();
diff --git a/tests/cts/net/src/android/net/cts/IpConfigurationTest.java b/tests/cts/net/src/android/net/cts/IpConfigurationTest.java
index 56ab2a7..385bf9e 100644
--- a/tests/cts/net/src/android/net/cts/IpConfigurationTest.java
+++ b/tests/cts/net/src/android/net/cts/IpConfigurationTest.java
@@ -16,7 +16,7 @@
package android.net.cts;
-import static com.android.testutils.ParcelUtils.assertParcelSane;
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -118,6 +118,6 @@
@Test
public void testParcel() {
final IpConfiguration config = new IpConfiguration();
- assertParcelSane(config, 4);
+ assertParcelingIsLossless(config);
}
}
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index 5c95aa3..7bce3d2 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -413,20 +413,26 @@
// Check that iface stats are within an acceptable range; data might be sent
// on the local interface by other apps.
- assertApproxEquals(
- ifaceTxBytes, newIfaceTxBytes, expectedTxByteDelta, ERROR_MARGIN_BYTES);
- assertApproxEquals(
- ifaceRxBytes, newIfaceRxBytes, expectedRxByteDelta, ERROR_MARGIN_BYTES);
- assertApproxEquals(
- ifaceTxPackets, newIfaceTxPackets, expectedTxPacketDelta, ERROR_MARGIN_PKTS);
- assertApproxEquals(
- ifaceRxPackets, newIfaceRxPackets, expectedRxPacketDelta, ERROR_MARGIN_PKTS);
+ assertApproxEquals("TX bytes", ifaceTxBytes, newIfaceTxBytes, expectedTxByteDelta,
+ ERROR_MARGIN_BYTES);
+ assertApproxEquals("RX bytes", ifaceRxBytes, newIfaceRxBytes, expectedRxByteDelta,
+ ERROR_MARGIN_BYTES);
+ assertApproxEquals("TX packets", ifaceTxPackets, newIfaceTxPackets,
+ expectedTxPacketDelta, ERROR_MARGIN_PKTS);
+ assertApproxEquals("RX packets", ifaceRxPackets, newIfaceRxPackets,
+ expectedRxPacketDelta, ERROR_MARGIN_PKTS);
}
private static void assertApproxEquals(
- long oldStats, long newStats, int expectedDelta, double errorMargin) {
- assertTrue(expectedDelta <= newStats - oldStats);
- assertTrue((expectedDelta * errorMargin) > newStats - oldStats);
+ String what, long oldStats, long newStats, int expectedDelta, double errorMargin) {
+ assertTrue(
+ "Expected at least " + expectedDelta + " " + what
+ + ", got " + (newStats - oldStats),
+ newStats - oldStats >= expectedDelta);
+ assertTrue(
+ "Expected at most " + errorMargin + " * " + expectedDelta + " " + what
+ + ", got " + (newStats - oldStats),
+ newStats - oldStats < (expectedDelta * errorMargin));
}
private static void initStatsChecker() throws Exception {
@@ -717,11 +723,10 @@
algoToRequiredMinSdk.put(AUTH_HMAC_SHA512, Build.VERSION_CODES.P);
algoToRequiredMinSdk.put(AUTH_CRYPT_AES_GCM, Build.VERSION_CODES.P);
- // TODO: b/170424293 Use Build.VERSION_CODES.S when is finalized
- algoToRequiredMinSdk.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
- algoToRequiredMinSdk.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
- algoToRequiredMinSdk.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
- algoToRequiredMinSdk.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+ algoToRequiredMinSdk.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
+ algoToRequiredMinSdk.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
+ algoToRequiredMinSdk.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
+ algoToRequiredMinSdk.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
final Set<String> supportedAlgos = IpSecAlgorithm.getSupportedAlgorithms();
diff --git a/tests/cts/net/src/android/net/cts/LocalSocketTest.java b/tests/cts/net/src/android/net/cts/LocalSocketTest.java
deleted file mode 100644
index 6e61705..0000000
--- a/tests/cts/net/src/android/net/cts/LocalSocketTest.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.cts;
-
-import junit.framework.TestCase;
-
-import android.net.Credentials;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.system.Os;
-import android.system.OsConstants;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-public class LocalSocketTest extends TestCase {
- private final static String ADDRESS_PREFIX = "com.android.net.LocalSocketTest";
-
- public void testLocalConnections() throws IOException {
- String address = ADDRESS_PREFIX + "_testLocalConnections";
- // create client and server socket
- LocalServerSocket localServerSocket = new LocalServerSocket(address);
- LocalSocket clientSocket = new LocalSocket();
-
- // establish connection between client and server
- LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
- assertFalse(clientSocket.isConnected());
- clientSocket.connect(locSockAddr);
- assertTrue(clientSocket.isConnected());
-
- LocalSocket serverSocket = localServerSocket.accept();
- assertTrue(serverSocket.isConnected());
- assertTrue(serverSocket.isBound());
- try {
- serverSocket.bind(localServerSocket.getLocalSocketAddress());
- fail("Cannot bind a LocalSocket from accept()");
- } catch (IOException expected) {
- }
- try {
- serverSocket.connect(locSockAddr);
- fail("Cannot connect a LocalSocket from accept()");
- } catch (IOException expected) {
- }
-
- Credentials credent = clientSocket.getPeerCredentials();
- assertTrue(0 != credent.getPid());
-
- // send data from client to server
- OutputStream clientOutStream = clientSocket.getOutputStream();
- clientOutStream.write(12);
- InputStream serverInStream = serverSocket.getInputStream();
- assertEquals(12, serverInStream.read());
-
- //send data from server to client
- OutputStream serverOutStream = serverSocket.getOutputStream();
- serverOutStream.write(3);
- InputStream clientInStream = clientSocket.getInputStream();
- assertEquals(3, clientInStream.read());
-
- // Test sending and receiving file descriptors
- clientSocket.setFileDescriptorsForSend(new FileDescriptor[]{FileDescriptor.in});
- clientOutStream.write(32);
- assertEquals(32, serverInStream.read());
-
- FileDescriptor[] out = serverSocket.getAncillaryFileDescriptors();
- assertEquals(1, out.length);
- FileDescriptor fd = clientSocket.getFileDescriptor();
- assertTrue(fd.valid());
-
- //shutdown input stream of client
- clientSocket.shutdownInput();
- assertEquals(-1, clientInStream.read());
-
- //shutdown output stream of client
- clientSocket.shutdownOutput();
- try {
- clientOutStream.write(10);
- fail("testLocalSocket shouldn't come to here");
- } catch (IOException e) {
- // expected
- }
-
- //shutdown input stream of server
- serverSocket.shutdownInput();
- assertEquals(-1, serverInStream.read());
-
- //shutdown output stream of server
- serverSocket.shutdownOutput();
- try {
- serverOutStream.write(10);
- fail("testLocalSocket shouldn't come to here");
- } catch (IOException e) {
- // expected
- }
-
- //close client socket
- clientSocket.close();
- try {
- clientInStream.read();
- fail("testLocalSocket shouldn't come to here");
- } catch (IOException e) {
- // expected
- }
-
- //close server socket
- serverSocket.close();
- try {
- serverInStream.read();
- fail("testLocalSocket shouldn't come to here");
- } catch (IOException e) {
- // expected
- }
- }
-
- public void testAccessors() throws IOException {
- String address = ADDRESS_PREFIX + "_testAccessors";
- LocalSocket socket = new LocalSocket();
- LocalSocketAddress addr = new LocalSocketAddress(address);
-
- assertFalse(socket.isBound());
- socket.bind(addr);
- assertTrue(socket.isBound());
- assertEquals(addr, socket.getLocalSocketAddress());
-
- String str = socket.toString();
- assertTrue(str.contains("impl:android.net.LocalSocketImpl"));
-
- socket.setReceiveBufferSize(1999);
- assertEquals(1999 << 1, socket.getReceiveBufferSize());
-
- socket.setSendBufferSize(3998);
- assertEquals(3998 << 1, socket.getSendBufferSize());
-
- assertEquals(0, socket.getSoTimeout());
- socket.setSoTimeout(1996);
- assertTrue(socket.getSoTimeout() > 0);
-
- try {
- socket.getRemoteSocketAddress();
- fail("testLocalSocketSecondary shouldn't come to here");
- } catch (UnsupportedOperationException e) {
- // expected
- }
-
- try {
- socket.isClosed();
- fail("testLocalSocketSecondary shouldn't come to here");
- } catch (UnsupportedOperationException e) {
- // expected
- }
-
- try {
- socket.isInputShutdown();
- fail("testLocalSocketSecondary shouldn't come to here");
- } catch (UnsupportedOperationException e) {
- // expected
- }
-
- try {
- socket.isOutputShutdown();
- fail("testLocalSocketSecondary shouldn't come to here");
- } catch (UnsupportedOperationException e) {
- // expected
- }
-
- try {
- socket.connect(addr, 2005);
- fail("testLocalSocketSecondary shouldn't come to here");
- } catch (UnsupportedOperationException e) {
- // expected
- }
-
- socket.close();
- }
-
- // http://b/31205169
- public void testSetSoTimeout_readTimeout() throws Exception {
- String address = ADDRESS_PREFIX + "_testSetSoTimeout_readTimeout";
-
- try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
- final LocalSocket clientSocket = socketPair.clientSocket;
-
- // Set the timeout in millis.
- int timeoutMillis = 1000;
- clientSocket.setSoTimeout(timeoutMillis);
-
- // Avoid blocking the test run if timeout doesn't happen by using a separate thread.
- Callable<Result> reader = () -> {
- try {
- clientSocket.getInputStream().read();
- return Result.noException("Did not block");
- } catch (IOException e) {
- return Result.exception(e);
- }
- };
- // Allow the configured timeout, plus some slop.
- int allowedTime = timeoutMillis + 2000;
- Result result = runInSeparateThread(allowedTime, reader);
-
- // Check the message was a timeout, it's all we have to go on.
- String expectedMessage = Os.strerror(OsConstants.EAGAIN);
- result.assertThrewIOException(expectedMessage);
- }
- }
-
- // http://b/31205169
- public void testSetSoTimeout_writeTimeout() throws Exception {
- String address = ADDRESS_PREFIX + "_testSetSoTimeout_writeTimeout";
-
- try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
- final LocalSocket clientSocket = socketPair.clientSocket;
-
- // Set the timeout in millis.
- int timeoutMillis = 1000;
- clientSocket.setSoTimeout(timeoutMillis);
-
- // Set a small buffer size so we know we can flood it.
- clientSocket.setSendBufferSize(100);
- final int bufferSize = clientSocket.getSendBufferSize();
-
- // Avoid blocking the test run if timeout doesn't happen by using a separate thread.
- Callable<Result> writer = () -> {
- try {
- byte[] toWrite = new byte[bufferSize * 2];
- clientSocket.getOutputStream().write(toWrite);
- return Result.noException("Did not block");
- } catch (IOException e) {
- return Result.exception(e);
- }
- };
- // Allow the configured timeout, plus some slop.
- int allowedTime = timeoutMillis + 2000;
-
- Result result = runInSeparateThread(allowedTime, writer);
-
- // Check the message was a timeout, it's all we have to go on.
- String expectedMessage = Os.strerror(OsConstants.EAGAIN);
- result.assertThrewIOException(expectedMessage);
- }
- }
-
- public void testAvailable() throws Exception {
- String address = ADDRESS_PREFIX + "_testAvailable";
-
- try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
- LocalSocket clientSocket = socketPair.clientSocket;
- LocalSocket serverSocket = socketPair.serverSocket.accept();
-
- OutputStream clientOutputStream = clientSocket.getOutputStream();
- InputStream serverInputStream = serverSocket.getInputStream();
- assertEquals(0, serverInputStream.available());
-
- byte[] buffer = new byte[50];
- clientOutputStream.write(buffer);
- assertEquals(50, serverInputStream.available());
-
- InputStream clientInputStream = clientSocket.getInputStream();
- OutputStream serverOutputStream = serverSocket.getOutputStream();
- assertEquals(0, clientInputStream.available());
- serverOutputStream.write(buffer);
- assertEquals(50, serverInputStream.available());
-
- serverSocket.close();
- }
- }
-
- // http://b/34095140
- public void testLocalSocketCreatedFromFileDescriptor() throws Exception {
- String address = ADDRESS_PREFIX + "_testLocalSocketCreatedFromFileDescriptor";
-
- // Establish connection between a local client and server to get a valid client socket file
- // descriptor.
- try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
- // Extract the client FileDescriptor we can use.
- FileDescriptor fileDescriptor = socketPair.clientSocket.getFileDescriptor();
- assertTrue(fileDescriptor.valid());
-
- // Create the LocalSocket we want to test.
- LocalSocket clientSocketCreatedFromFileDescriptor =
- LocalSocket.createConnectedLocalSocket(fileDescriptor);
- assertTrue(clientSocketCreatedFromFileDescriptor.isConnected());
- assertTrue(clientSocketCreatedFromFileDescriptor.isBound());
-
- // Test the LocalSocket can be used for communication.
- LocalSocket serverSocket = socketPair.serverSocket.accept();
- OutputStream clientOutputStream =
- clientSocketCreatedFromFileDescriptor.getOutputStream();
- InputStream serverInputStream = serverSocket.getInputStream();
-
- clientOutputStream.write(12);
- assertEquals(12, serverInputStream.read());
-
- // Closing clientSocketCreatedFromFileDescriptor does not close the file descriptor.
- clientSocketCreatedFromFileDescriptor.close();
- assertTrue(fileDescriptor.valid());
-
- // .. while closing the LocalSocket that owned the file descriptor does.
- socketPair.clientSocket.close();
- assertFalse(fileDescriptor.valid());
- }
- }
-
- public void testFlush() throws Exception {
- String address = ADDRESS_PREFIX + "_testFlush";
-
- try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
- LocalSocket clientSocket = socketPair.clientSocket;
- LocalSocket serverSocket = socketPair.serverSocket.accept();
-
- OutputStream clientOutputStream = clientSocket.getOutputStream();
- InputStream serverInputStream = serverSocket.getInputStream();
- testFlushWorks(clientOutputStream, serverInputStream);
-
- OutputStream serverOutputStream = serverSocket.getOutputStream();
- InputStream clientInputStream = clientSocket.getInputStream();
- testFlushWorks(serverOutputStream, clientInputStream);
-
- serverSocket.close();
- }
- }
-
- private void testFlushWorks(OutputStream outputStream, InputStream inputStream)
- throws Exception {
- final int bytesToTransfer = 50;
- StreamReader inputStreamReader = new StreamReader(inputStream, bytesToTransfer);
-
- byte[] buffer = new byte[bytesToTransfer];
- outputStream.write(buffer);
- assertEquals(bytesToTransfer, inputStream.available());
-
- // Start consuming the data.
- inputStreamReader.start();
-
- // This doesn't actually flush any buffers, it just polls until the reader has read all the
- // bytes.
- outputStream.flush();
-
- inputStreamReader.waitForCompletion(5000);
- inputStreamReader.assertBytesRead(bytesToTransfer);
- assertEquals(0, inputStream.available());
- }
-
- private static class StreamReader extends Thread {
- private final InputStream is;
- private final int expectedByteCount;
- private final CountDownLatch completeLatch = new CountDownLatch(1);
-
- private volatile Exception exception;
- private int bytesRead;
-
- private StreamReader(InputStream is, int expectedByteCount) {
- this.is = is;
- this.expectedByteCount = expectedByteCount;
- }
-
- @Override
- public void run() {
- try {
- byte[] buffer = new byte[10];
- int readCount;
- while ((readCount = is.read(buffer)) >= 0) {
- bytesRead += readCount;
- if (bytesRead >= expectedByteCount) {
- break;
- }
- }
- } catch (IOException e) {
- exception = e;
- } finally {
- completeLatch.countDown();
- }
- }
-
- public void waitForCompletion(long waitMillis) throws Exception {
- if (!completeLatch.await(waitMillis, TimeUnit.MILLISECONDS)) {
- fail("Timeout waiting for completion");
- }
- if (exception != null) {
- throw new Exception("Read failed", exception);
- }
- }
-
- public void assertBytesRead(int expected) {
- assertEquals(expected, bytesRead);
- }
- }
-
- private static class Result {
- private final String type;
- private final Exception e;
-
- private Result(String type, Exception e) {
- this.type = type;
- this.e = e;
- }
-
- static Result noException(String description) {
- return new Result(description, null);
- }
-
- static Result exception(Exception e) {
- return new Result(e.getClass().getName(), e);
- }
-
- void assertThrewIOException(String expectedMessage) {
- assertEquals("Unexpected result type", IOException.class.getName(), type);
- assertEquals("Unexpected exception message", expectedMessage, e.getMessage());
- }
- }
-
- private static Result runInSeparateThread(int allowedTime, final Callable<Result> callable)
- throws Exception {
- ExecutorService service = Executors.newSingleThreadScheduledExecutor();
- Future<Result> future = service.submit(callable);
- Result result = future.get(allowedTime, TimeUnit.MILLISECONDS);
- if (!future.isDone()) {
- fail("Worker thread appears blocked");
- }
- return result;
- }
-
- private static class LocalSocketPair implements AutoCloseable {
- static LocalSocketPair createConnectedSocketPair(String address) throws Exception {
- LocalServerSocket localServerSocket = new LocalServerSocket(address);
- final LocalSocket clientSocket = new LocalSocket();
-
- // Establish connection between client and server
- LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
- clientSocket.connect(locSockAddr);
- assertTrue(clientSocket.isConnected());
- return new LocalSocketPair(localServerSocket, clientSocket);
- }
-
- final LocalServerSocket serverSocket;
- final LocalSocket clientSocket;
-
- LocalSocketPair(LocalServerSocket serverSocket, LocalSocket clientSocket) {
- this.serverSocket = serverSocket;
- this.clientSocket = clientSocket;
- }
-
- public void close() throws Exception {
- serverSocket.close();
- clientSocket.close();
- }
- }
-}
diff --git a/tests/cts/net/src/android/net/cts/MacAddressTest.java b/tests/cts/net/src/android/net/cts/MacAddressTest.java
index 3fd3bba..e47155b 100644
--- a/tests/cts/net/src/android/net/cts/MacAddressTest.java
+++ b/tests/cts/net/src/android/net/cts/MacAddressTest.java
@@ -20,7 +20,7 @@
import static android.net.MacAddress.TYPE_MULTICAST;
import static android.net.MacAddress.TYPE_UNICAST;
-import static com.android.testutils.ParcelUtils.assertParcelSane;
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
@@ -218,6 +218,6 @@
public void testParcelMacAddress() {
final MacAddress mac = MacAddress.fromString("52:74:f2:b1:a8:7f");
- assertParcelSane(mac, 1);
+ assertParcelingIsLossless(mac);
}
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 7c380e3..ef5dc77 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -61,20 +61,6 @@
import android.net.Uri
import android.net.VpnManager
import android.net.VpnTransportInfo
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRegisterQosCallback
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSignalStrengthThresholdsUpdated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnError
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionAvailable
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionLost
@@ -98,6 +84,20 @@
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
+import com.android.testutils.TestableNetworkAgent
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnRegisterQosCallback
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import com.android.testutils.TestableNetworkCallback
import org.junit.After
import org.junit.Assert.assertArrayEquals
@@ -136,10 +136,6 @@
// and then there is the Binder call), so have a short timeout for this as it will be
// exhausted every time.
private const val NO_CALLBACK_TIMEOUT = 200L
-// Any legal score (0~99) for the test network would do, as it is going to be kept up by the
-// requests filed by the test and should never match normal internet requests. 70 is the default
-// score of Ethernet networks, it's as good a value as any other.
-private const val TEST_NETWORK_SCORE = 70
private const val WORSE_NETWORK_SCORE = 65
private const val BETTER_NETWORK_SCORE = 75
private const val FAKE_NET_ID = 1098
@@ -165,10 +161,6 @@
private val mCM = realContext.getSystemService(ConnectivityManager::class.java)!!
private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
private val mFakeConnectivityService = FakeConnectivityService()
-
- private class Provider(context: Context, looper: Looper) :
- NetworkProvider(context, looper, "NetworkAgentTest NetworkProvider")
-
private val agentsToCleanUp = mutableListOf<NetworkAgent>()
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
private var qosTestSocket: Socket? = null
@@ -219,146 +211,6 @@
fun disconnect() = agent.onDisconnected()
}
- private open class TestableNetworkAgent(
- context: Context,
- looper: Looper,
- val nc: NetworkCapabilities,
- val lp: LinkProperties,
- conf: NetworkAgentConfig
- ) : NetworkAgent(context, looper, TestableNetworkAgent::class.java.simpleName /* tag */,
- nc, lp, TEST_NETWORK_SCORE, conf, Provider(context, looper)) {
- private val history = ArrayTrackRecord<CallbackEntry>().newReadHead()
-
- sealed class CallbackEntry {
- object OnBandwidthUpdateRequested : CallbackEntry()
- object OnNetworkUnwanted : CallbackEntry()
- data class OnAddKeepalivePacketFilter(
- val slot: Int,
- val packet: KeepalivePacketData
- ) : CallbackEntry()
- data class OnRemoveKeepalivePacketFilter(val slot: Int) : CallbackEntry()
- data class OnStartSocketKeepalive(
- val slot: Int,
- val interval: Int,
- val packet: KeepalivePacketData
- ) : CallbackEntry()
- data class OnStopSocketKeepalive(val slot: Int) : CallbackEntry()
- data class OnSaveAcceptUnvalidated(val accept: Boolean) : CallbackEntry()
- object OnAutomaticReconnectDisabled : CallbackEntry()
- data class OnValidationStatus(val status: Int, val uri: Uri?) : CallbackEntry()
- data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
- object OnNetworkCreated : CallbackEntry()
- object OnNetworkDestroyed : CallbackEntry()
- data class OnRegisterQosCallback(
- val callbackId: Int,
- val filter: QosFilter
- ) : CallbackEntry()
- data class OnUnregisterQosCallback(val callbackId: Int) : CallbackEntry()
- }
-
- override fun onBandwidthUpdateRequested() {
- history.add(OnBandwidthUpdateRequested)
- }
-
- override fun onNetworkUnwanted() {
- history.add(OnNetworkUnwanted)
- }
-
- override fun onAddKeepalivePacketFilter(slot: Int, packet: KeepalivePacketData) {
- history.add(OnAddKeepalivePacketFilter(slot, packet))
- }
-
- override fun onRemoveKeepalivePacketFilter(slot: Int) {
- history.add(OnRemoveKeepalivePacketFilter(slot))
- }
-
- override fun onStartSocketKeepalive(
- slot: Int,
- interval: Duration,
- packet: KeepalivePacketData
- ) {
- history.add(OnStartSocketKeepalive(slot, interval.seconds.toInt(), packet))
- }
-
- override fun onStopSocketKeepalive(slot: Int) {
- history.add(OnStopSocketKeepalive(slot))
- }
-
- override fun onSaveAcceptUnvalidated(accept: Boolean) {
- history.add(OnSaveAcceptUnvalidated(accept))
- }
-
- override fun onAutomaticReconnectDisabled() {
- history.add(OnAutomaticReconnectDisabled)
- }
-
- override fun onSignalStrengthThresholdsUpdated(thresholds: IntArray) {
- history.add(OnSignalStrengthThresholdsUpdated(thresholds))
- }
-
- fun expectSignalStrengths(thresholds: IntArray? = intArrayOf()) {
- expectCallback<OnSignalStrengthThresholdsUpdated>().let {
- assertArrayEquals(thresholds, it.thresholds)
- }
- }
-
- override fun onQosCallbackRegistered(qosCallbackId: Int, filter: QosFilter) {
- history.add(OnRegisterQosCallback(qosCallbackId, filter))
- }
-
- override fun onQosCallbackUnregistered(qosCallbackId: Int) {
- history.add(OnUnregisterQosCallback(qosCallbackId))
- }
-
- override fun onValidationStatus(status: Int, uri: Uri?) {
- history.add(OnValidationStatus(status, uri))
- }
-
- override fun onNetworkCreated() {
- history.add(OnNetworkCreated)
- }
-
- override fun onNetworkDestroyed() {
- history.add(OnNetworkDestroyed)
- }
-
- // Expects the initial validation event that always occurs immediately after registering
- // a NetworkAgent whose network does not require validation (which test networks do
- // not, since they lack the INTERNET capability). It always contains the default argument
- // for the URI.
- fun expectValidationBypassedStatus() = expectCallback<OnValidationStatus>().let {
- assertEquals(it.status, VALID_NETWORK)
- // The returned Uri is parsed from the empty string, which means it's an
- // instance of the (private) Uri.StringUri. There are no real good ways
- // to check this, the least bad is to just convert it to a string and
- // make sure it's empty.
- assertEquals("", it.uri.toString())
- }
-
- inline fun <reified T : CallbackEntry> expectCallback(): T {
- val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
- assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
- return foundCallback
- }
-
- inline fun <reified T : CallbackEntry> expectCallback(valid: (T) -> Boolean) {
- val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
- assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
- assertTrue(valid(foundCallback), "Unexpected callback : $foundCallback")
- }
-
- inline fun <reified T : CallbackEntry> eventuallyExpect() =
- history.poll(DEFAULT_TIMEOUT_MS) { it is T }.also {
- assertNotNull(it, "Callback ${T::class} not received")
- } as T
-
- fun assertNoCallback() {
- assertTrue(waitForIdle(DEFAULT_TIMEOUT_MS),
- "Handler didn't became idle after ${DEFAULT_TIMEOUT_MS}ms")
- assertNull(history.peek())
- }
- }
-
private fun requestNetwork(request: NetworkRequest, callback: TestableNetworkCallback) {
mCM.requestNetwork(request, callback)
callbacksToCleanUp.add(callback)
@@ -1072,6 +924,7 @@
Executors.newSingleThreadExecutor().let { executor ->
try {
val info = QosSocketInfo(agent.network!!, socket)
+ assertEquals(agent.network, info.getNetwork())
mCM.registerQosCallback(info, executor, qosCallback)
val callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
diff --git a/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt b/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
index fa15e8f..d6120f8 100644
--- a/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
@@ -26,16 +26,19 @@
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
+import com.android.modules.utils.build.SdkLevel
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
import org.junit.Rule
import org.junit.runner.RunWith
import org.junit.Test
+import kotlin.reflect.jvm.isAccessible
+import kotlin.test.assertFails
+import kotlin.test.assertFailsWith
const val TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE
const val TYPE_WIFI = ConnectivityManager.TYPE_WIFI
@@ -97,12 +100,16 @@
assertNull(networkInfo.reason)
assertNull(networkInfo.extraInfo)
- try {
+ assertFailsWith<IllegalArgumentException> {
NetworkInfo(ConnectivityManager.MAX_NETWORK_TYPE + 1,
TelephonyManager.NETWORK_TYPE_LTE, MOBILE_TYPE_NAME, LTE_SUBTYPE_NAME)
- fail("Unexpected behavior. Network type is invalid.")
- } catch (e: IllegalArgumentException) {
- // Expected behavior.
+ }
+
+ if (SdkLevel.isAtLeastT()) {
+ assertFailsWith<NullPointerException> { NetworkInfo(null) }
+ } else {
+ // Doesn't immediately crash on S-
+ NetworkInfo(null)
}
}
@@ -118,5 +125,29 @@
assertEquals(State.CONNECTED, networkInfo.state)
assertEquals(reason, networkInfo.reason)
assertEquals(extraReason, networkInfo.extraInfo)
+
+ // Create an incorrect enum value by calling the default constructor of the enum
+ val constructor = DetailedState::class.java.declaredConstructors.first {
+ it.parameters.size == 2
+ }
+ constructor.isAccessible = true
+ val incorrectDetailedState = constructor.newInstance("any", 200) as DetailedState
+ if (SdkLevel.isAtLeastT()) {
+ assertFailsWith<NullPointerException> {
+ NetworkInfo(null)
+ }
+ assertFailsWith<NullPointerException> {
+ networkInfo.setDetailedState(null, "reason", "extraInfo")
+ }
+ // This actually throws ArrayOutOfBoundsException because of the implementation of
+ // EnumMap, but that's an implementation detail so accept any crash.
+ assertFails {
+ networkInfo.setDetailedState(incorrectDetailedState, "reason", "extraInfo")
+ }
+ } else {
+ // Doesn't immediately crash on S-
+ NetworkInfo(null)
+ networkInfo.setDetailedState(null, "reason", "extraInfo")
+ }
}
-}
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
index 63863da..637ed26 100644
--- a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -310,6 +311,43 @@
request.networkCapabilities.satisfiedByNetworkCapabilities(nc));
}
+ private static Set<Range<Integer>> uidRangesForUid(int uid) {
+ final Range<Integer> range = new Range<>(uid, uid);
+ return Set.of(range);
+ }
+
+ @Test
+ public void testSetIncludeOtherUidNetworks() throws Exception {
+ assumeTrue(TestUtils.shouldTestSApis());
+ final NetworkRequestShim shim = NetworkRequestShimImpl.newInstance();
+
+ final NetworkRequest.Builder builder = new NetworkRequest.Builder();
+ // NetworkRequests have NET_CAPABILITY_NOT_VCN_MANAGED by default.
+ builder.removeCapability(ConstantsShim.NET_CAPABILITY_NOT_VCN_MANAGED);
+ shim.setIncludeOtherUidNetworks(builder, false);
+ final NetworkRequest request = builder.build();
+
+ final NetworkRequest.Builder otherUidsBuilder = new NetworkRequest.Builder();
+ otherUidsBuilder.removeCapability(ConstantsShim.NET_CAPABILITY_NOT_VCN_MANAGED);
+ shim.setIncludeOtherUidNetworks(otherUidsBuilder, true);
+ final NetworkRequest otherUidsRequest = otherUidsBuilder.build();
+
+ assertNotEquals(Process.SYSTEM_UID, Process.myUid());
+ final NetworkCapabilities ncWithMyUid = new NetworkCapabilities()
+ .setUids(uidRangesForUid(Process.myUid()));
+ final NetworkCapabilities ncWithOtherUid = new NetworkCapabilities()
+ .setUids(uidRangesForUid(Process.SYSTEM_UID));
+
+ assertTrue(request + " should be satisfied by " + ncWithMyUid,
+ request.canBeSatisfiedBy(ncWithMyUid));
+ assertTrue(otherUidsRequest + " should be satisfied by " + ncWithMyUid,
+ otherUidsRequest.canBeSatisfiedBy(ncWithMyUid));
+ assertFalse(request + " should not be satisfied by " + ncWithOtherUid,
+ request.canBeSatisfiedBy(ncWithOtherUid));
+ assertTrue(otherUidsRequest + " should be satisfied by " + ncWithOtherUid,
+ otherUidsRequest.canBeSatisfiedBy(ncWithOtherUid));
+ }
+
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testRequestorUid() {
final NetworkCapabilities nc = new NetworkCapabilities();
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
index 5290f0d..8e98dba 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
@@ -25,7 +25,6 @@
import android.net.InetAddresses
import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
-import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkCapabilities.TRANSPORT_TEST
import android.net.NetworkRequest
import android.net.TestNetworkInterface
@@ -96,7 +95,6 @@
private val ethRequest = NetworkRequest.Builder()
// ETHERNET|TEST transport networks do not have NET_CAPABILITY_TRUSTED
.removeCapability(NET_CAPABILITY_TRUSTED)
- .addTransportType(TRANSPORT_ETHERNET)
.addTransportType(TRANSPORT_TEST).build()
private val ethRequestCb = TestableNetworkCallback()
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
index dde14ac..391d03a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
@@ -19,12 +19,20 @@
import android.Manifest
import android.net.util.NetworkStackUtils
import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import android.util.Log
import com.android.testutils.runAsShell
+import com.android.testutils.tryTest
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
/**
* Collection of utility methods for configuring network validation.
*/
internal object NetworkValidationTestUtil {
+ val TAG = NetworkValidationTestUtil::class.simpleName
+ const val TIMEOUT_MS = 20_000L
/**
* Clear the test network validation URLs.
@@ -59,10 +67,52 @@
@JvmStatic fun setUrlExpirationDeviceConfig(timestamp: Long?) =
setConfig(NetworkStackUtils.TEST_URL_EXPIRATION_TIME, timestamp?.toString())
- private fun setConfig(configKey: String, value: String?) {
- runAsShell(Manifest.permission.WRITE_DEVICE_CONFIG) {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_CONNECTIVITY, configKey, value, false /* makeDefault */)
+ private fun setConfig(configKey: String, value: String?): String? {
+ Log.i(TAG, "Setting config \"$configKey\" to \"$value\"")
+ val readWritePermissions = arrayOf(
+ Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.WRITE_DEVICE_CONFIG)
+
+ val existingValue = runAsShell(*readWritePermissions) {
+ DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, configKey)
+ }
+ if (existingValue == value) {
+ // Already the correct value. There may be a race if a change is already in flight,
+ // but if multiple threads update the config there is no way to fix that anyway.
+ Log.i(TAG, "\$configKey\" already had value \"$value\"")
+ return value
+ }
+
+ val future = CompletableFuture<String>()
+ val listener = DeviceConfig.OnPropertiesChangedListener {
+ // The listener receives updates for any change to any key, so don't react to
+ // changes that do not affect the relevant key
+ if (!it.keyset.contains(configKey)) return@OnPropertiesChangedListener
+ if (it.getString(configKey, null) == value) {
+ future.complete(value)
+ }
+ }
+
+ return tryTest {
+ runAsShell(*readWritePermissions) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_CONNECTIVITY,
+ inlineExecutor,
+ listener)
+ DeviceConfig.setProperty(
+ NAMESPACE_CONNECTIVITY,
+ configKey,
+ value,
+ false /* makeDefault */)
+ // Don't drop the permission until the config is applied, just in case
+ future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }.also {
+ Log.i(TAG, "Config \"$configKey\" successfully set to \"$value\"")
+ }
+ } cleanup {
+ DeviceConfig.removeOnPropertiesChangedListener(listener)
}
}
-}
\ No newline at end of file
+
+ private val inlineExecutor get() = Executor { r -> r.run() }
+}
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.java b/tests/cts/net/src/android/net/cts/NsdManagerTest.java
deleted file mode 100644
index 2bcfdc3..0000000
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.java
+++ /dev/null
@@ -1,594 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.cts;
-
-import android.content.Context;
-import android.net.nsd.NsdManager;
-import android.net.nsd.NsdServiceInfo;
-import android.platform.test.annotations.AppModeFull;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.util.Arrays;
-import java.util.Random;
-import java.util.List;
-import java.util.ArrayList;
-
-@AppModeFull(reason = "Socket cannot bind in instant app mode")
-public class NsdManagerTest extends AndroidTestCase {
-
- private static final String TAG = "NsdManagerTest";
- private static final String SERVICE_TYPE = "_nmt._tcp";
- private static final int TIMEOUT = 2000;
-
- private static final boolean DBG = false;
-
- NsdManager mNsdManager;
-
- NsdManager.RegistrationListener mRegistrationListener;
- NsdManager.DiscoveryListener mDiscoveryListener;
- NsdManager.ResolveListener mResolveListener;
- private NsdServiceInfo mResolvedService;
-
- public NsdManagerTest() {
- initRegistrationListener();
- initDiscoveryListener();
- initResolveListener();
- }
-
- private void initRegistrationListener() {
- mRegistrationListener = new NsdManager.RegistrationListener() {
- @Override
- public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
- setEvent("onRegistrationFailed", errorCode);
- }
-
- @Override
- public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
- setEvent("onUnregistrationFailed", errorCode);
- }
-
- @Override
- public void onServiceRegistered(NsdServiceInfo serviceInfo) {
- setEvent("onServiceRegistered", serviceInfo);
- }
-
- @Override
- public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
- setEvent("onServiceUnregistered", serviceInfo);
- }
- };
- }
-
- private void initDiscoveryListener() {
- mDiscoveryListener = new NsdManager.DiscoveryListener() {
- @Override
- public void onStartDiscoveryFailed(String serviceType, int errorCode) {
- setEvent("onStartDiscoveryFailed", errorCode);
- }
-
- @Override
- public void onStopDiscoveryFailed(String serviceType, int errorCode) {
- setEvent("onStopDiscoveryFailed", errorCode);
- }
-
- @Override
- public void onDiscoveryStarted(String serviceType) {
- NsdServiceInfo info = new NsdServiceInfo();
- info.setServiceType(serviceType);
- setEvent("onDiscoveryStarted", info);
- }
-
- @Override
- public void onDiscoveryStopped(String serviceType) {
- NsdServiceInfo info = new NsdServiceInfo();
- info.setServiceType(serviceType);
- setEvent("onDiscoveryStopped", info);
- }
-
- @Override
- public void onServiceFound(NsdServiceInfo serviceInfo) {
- setEvent("onServiceFound", serviceInfo);
- }
-
- @Override
- public void onServiceLost(NsdServiceInfo serviceInfo) {
- setEvent("onServiceLost", serviceInfo);
- }
- };
- }
-
- private void initResolveListener() {
- mResolveListener = new NsdManager.ResolveListener() {
- @Override
- public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
- setEvent("onResolveFailed", errorCode);
- }
-
- @Override
- public void onServiceResolved(NsdServiceInfo serviceInfo) {
- mResolvedService = serviceInfo;
- setEvent("onServiceResolved", serviceInfo);
- }
- };
- }
-
-
-
- private final class EventData {
- EventData(String callbackName, NsdServiceInfo info) {
- mCallbackName = callbackName;
- mSucceeded = true;
- mErrorCode = 0;
- mInfo = info;
- }
- EventData(String callbackName, int errorCode) {
- mCallbackName = callbackName;
- mSucceeded = false;
- mErrorCode = errorCode;
- mInfo = null;
- }
- private final String mCallbackName;
- private final boolean mSucceeded;
- private final int mErrorCode;
- private final NsdServiceInfo mInfo;
- }
-
- private final List<EventData> mEventCache = new ArrayList<EventData>();
-
- private void setEvent(String callbackName, int errorCode) {
- if (DBG) Log.d(TAG, callbackName + " failed with " + String.valueOf(errorCode));
- EventData eventData = new EventData(callbackName, errorCode);
- synchronized (mEventCache) {
- mEventCache.add(eventData);
- mEventCache.notify();
- }
- }
-
- private void setEvent(String callbackName, NsdServiceInfo info) {
- if (DBG) Log.d(TAG, "Received event " + callbackName + " for " + info.getServiceName());
- EventData eventData = new EventData(callbackName, info);
- synchronized (mEventCache) {
- mEventCache.add(eventData);
- mEventCache.notify();
- }
- }
-
- void clearEventCache() {
- synchronized(mEventCache) {
- mEventCache.clear();
- }
- }
-
- int eventCacheSize() {
- synchronized(mEventCache) {
- return mEventCache.size();
- }
- }
-
- private int mWaitId = 0;
- private EventData waitForCallback(String callbackName) {
-
- synchronized(mEventCache) {
-
- mWaitId ++;
- if (DBG) Log.d(TAG, "Waiting for " + callbackName + ", id=" + String.valueOf(mWaitId));
-
- try {
- long startTime = android.os.SystemClock.uptimeMillis();
- long elapsedTime = 0;
- int index = 0;
- while (elapsedTime < TIMEOUT ) {
- // first check if we've received that event
- for (; index < mEventCache.size(); index++) {
- EventData e = mEventCache.get(index);
- if (e.mCallbackName.equals(callbackName)) {
- if (DBG) Log.d(TAG, "exiting wait id=" + String.valueOf(mWaitId));
- return e;
- }
- }
-
- // Not yet received, just wait
- mEventCache.wait(TIMEOUT - elapsedTime);
- elapsedTime = android.os.SystemClock.uptimeMillis() - startTime;
- }
- // we exited the loop because of TIMEOUT; fail the call
- if (DBG) Log.d(TAG, "timed out waiting id=" + String.valueOf(mWaitId));
- return null;
- } catch (InterruptedException e) {
- return null; // wait timed out!
- }
- }
- }
-
- private EventData waitForNewEvents() throws InterruptedException {
- if (DBG) Log.d(TAG, "Waiting for a bit, id=" + String.valueOf(mWaitId));
-
- long startTime = android.os.SystemClock.uptimeMillis();
- long elapsedTime = 0;
- synchronized (mEventCache) {
- int index = mEventCache.size();
- while (elapsedTime < TIMEOUT ) {
- // first check if we've received that event
- for (; index < mEventCache.size(); index++) {
- EventData e = mEventCache.get(index);
- return e;
- }
-
- // Not yet received, just wait
- mEventCache.wait(TIMEOUT - elapsedTime);
- elapsedTime = android.os.SystemClock.uptimeMillis() - startTime;
- }
- }
-
- return null;
- }
-
- private String mServiceName;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- if (DBG) Log.d(TAG, "Setup test ...");
- mNsdManager = (NsdManager) getContext().getSystemService(Context.NSD_SERVICE);
-
- Random rand = new Random();
- mServiceName = new String("NsdTest");
- for (int i = 0; i < 4; i++) {
- mServiceName = mServiceName + String.valueOf(rand.nextInt(10));
- }
- }
-
- @Override
- public void tearDown() throws Exception {
- if (DBG) Log.d(TAG, "Tear down test ...");
- super.tearDown();
- }
-
- public void testNDSManager() throws Exception {
- EventData lastEvent = null;
-
- if (DBG) Log.d(TAG, "Starting test ...");
-
- NsdServiceInfo si = new NsdServiceInfo();
- si.setServiceType(SERVICE_TYPE);
- si.setServiceName(mServiceName);
-
- byte testByteArray[] = new byte[] {-128, 127, 2, 1, 0, 1, 2};
- String String256 = "1_________2_________3_________4_________5_________6_________" +
- "7_________8_________9_________10________11________12________13________" +
- "14________15________16________17________18________19________20________" +
- "21________22________23________24________25________123456";
-
- // Illegal attributes
- try {
- si.setAttribute(null, (String) null);
- fail("Could set null key");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute("", (String) null);
- fail("Could set empty key");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute(String256, (String) null);
- fail("Could set key with 255 characters");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute("key", String256.substring(3));
- fail("Could set key+value combination with more than 255 characters");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute("key", String256.substring(4));
- fail("Could set key+value combination with 255 characters");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute(new String(new byte[]{0x19}), (String) null);
- fail("Could set key with invalid character");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute("=", (String) null);
- fail("Could set key with invalid character");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute(new String(new byte[]{0x7F}), (String) null);
- fail("Could set key with invalid character");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- // Allowed attributes
- si.setAttribute("booleanAttr", (String) null);
- si.setAttribute("keyValueAttr", "value");
- si.setAttribute("keyEqualsAttr", "=");
- si.setAttribute(" whiteSpaceKeyValueAttr ", " value ");
- si.setAttribute("binaryDataAttr", testByteArray);
- si.setAttribute("nullBinaryDataAttr", (byte[]) null);
- si.setAttribute("emptyBinaryDataAttr", new byte[]{});
- si.setAttribute("longkey", String256.substring(9));
-
- ServerSocket socket;
- int localPort;
-
- try {
- socket = new ServerSocket(0);
- localPort = socket.getLocalPort();
- si.setPort(localPort);
- } catch (IOException e) {
- if (DBG) Log.d(TAG, "Could not open a local socket");
- assertTrue(false);
- return;
- }
-
- if (DBG) Log.d(TAG, "Port = " + String.valueOf(localPort));
-
- clearEventCache();
-
- mNsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
- lastEvent = waitForCallback("onServiceRegistered"); // id = 1
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
- assertTrue(eventCacheSize() == 1);
-
- // We may not always get the name that we tried to register;
- // This events tells us the name that was registered.
- String registeredName = lastEvent.mInfo.getServiceName();
- si.setServiceName(registeredName);
-
- clearEventCache();
-
- mNsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
- mDiscoveryListener);
-
- // Expect discovery started
- lastEvent = waitForCallback("onDiscoveryStarted"); // id = 2
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- // Remove this event, so accounting becomes easier later
- synchronized (mEventCache) {
- mEventCache.remove(lastEvent);
- }
-
- // Expect a service record to be discovered (and filter the ones
- // that are unrelated to this test)
- boolean found = false;
- for (int i = 0; i < 32; i++) {
-
- lastEvent = waitForCallback("onServiceFound"); // id = 3
- if (lastEvent == null) {
- // no more onServiceFound events are being reported!
- break;
- }
-
- assertTrue(lastEvent.mSucceeded);
-
- if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " +
- lastEvent.mInfo.getServiceName());
-
- if (lastEvent.mInfo.getServiceName().equals(registeredName)) {
- // Save it, as it will get overwritten with new serviceFound events
- si = lastEvent.mInfo;
- found = true;
- }
-
- // Remove this event from the event cache, so it won't be found by subsequent
- // calls to waitForCallback
- synchronized (mEventCache) {
- mEventCache.remove(lastEvent);
- }
- }
-
- assertTrue(found);
-
- // We've removed all serviceFound events, and we've removed the discoveryStarted
- // event as well, so now the event cache should be empty!
- assertTrue(eventCacheSize() == 0);
-
- // Resolve the service
- clearEventCache();
- mNsdManager.resolveService(si, mResolveListener);
- lastEvent = waitForCallback("onServiceResolved"); // id = 4
-
- assertNotNull(mResolvedService);
-
- // Check Txt attributes
- assertEquals(8, mResolvedService.getAttributes().size());
- assertTrue(mResolvedService.getAttributes().containsKey("booleanAttr"));
- assertNull(mResolvedService.getAttributes().get("booleanAttr"));
- assertEquals("value", new String(mResolvedService.getAttributes().get("keyValueAttr")));
- assertEquals("=", new String(mResolvedService.getAttributes().get("keyEqualsAttr")));
- assertEquals(" value ", new String(mResolvedService.getAttributes()
- .get(" whiteSpaceKeyValueAttr ")));
- assertEquals(String256.substring(9), new String(mResolvedService.getAttributes()
- .get("longkey")));
- assertTrue(Arrays.equals(testByteArray,
- mResolvedService.getAttributes().get("binaryDataAttr")));
- assertTrue(mResolvedService.getAttributes().containsKey("nullBinaryDataAttr"));
- assertNull(mResolvedService.getAttributes().get("nullBinaryDataAttr"));
- assertTrue(mResolvedService.getAttributes().containsKey("emptyBinaryDataAttr"));
- assertNull(mResolvedService.getAttributes().get("emptyBinaryDataAttr"));
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": Port = " +
- String.valueOf(lastEvent.mInfo.getPort()));
-
- assertTrue(lastEvent.mInfo.getPort() == localPort);
- assertTrue(eventCacheSize() == 1);
-
- checkForAdditionalEvents();
- clearEventCache();
-
- // Unregister the service
- mNsdManager.unregisterService(mRegistrationListener);
- lastEvent = waitForCallback("onServiceUnregistered"); // id = 5
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- // Expect a callback for service lost
- lastEvent = waitForCallback("onServiceLost"); // id = 6
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
-
- // Register service again to see if we discover it
- checkForAdditionalEvents();
- clearEventCache();
-
- si = new NsdServiceInfo();
- si.setServiceType(SERVICE_TYPE);
- si.setServiceName(mServiceName);
- si.setPort(localPort);
-
- // Create a new registration listener and register same service again
- initRegistrationListener();
-
- mNsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
-
- lastEvent = waitForCallback("onServiceRegistered"); // id = 7
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- registeredName = lastEvent.mInfo.getServiceName();
-
- // Expect a record to be discovered
- // Expect a service record to be discovered (and filter the ones
- // that are unrelated to this test)
- found = false;
- for (int i = 0; i < 32; i++) {
-
- lastEvent = waitForCallback("onServiceFound"); // id = 8
- if (lastEvent == null) {
- // no more onServiceFound events are being reported!
- break;
- }
-
- assertTrue(lastEvent.mSucceeded);
-
- if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " +
- lastEvent.mInfo.getServiceName());
-
- if (lastEvent.mInfo.getServiceName().equals(registeredName)) {
- // Save it, as it will get overwritten with new serviceFound events
- si = lastEvent.mInfo;
- found = true;
- }
-
- // Remove this event from the event cache, so it won't be found by subsequent
- // calls to waitForCallback
- synchronized (mEventCache) {
- mEventCache.remove(lastEvent);
- }
- }
-
- assertTrue(found);
-
- // Resolve the service
- clearEventCache();
- mNsdManager.resolveService(si, mResolveListener);
- lastEvent = waitForCallback("onServiceResolved"); // id = 9
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " +
- lastEvent.mInfo.getServiceName());
-
- assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
-
- assertNotNull(mResolvedService);
-
- // Check that we don't have any TXT records
- assertEquals(0, mResolvedService.getAttributes().size());
-
- checkForAdditionalEvents();
- clearEventCache();
-
- mNsdManager.stopServiceDiscovery(mDiscoveryListener);
- lastEvent = waitForCallback("onDiscoveryStopped"); // id = 10
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
- assertTrue(checkCacheSize(1));
-
- checkForAdditionalEvents();
- clearEventCache();
-
- mNsdManager.unregisterService(mRegistrationListener);
-
- lastEvent = waitForCallback("onServiceUnregistered"); // id = 11
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
- assertTrue(checkCacheSize(1));
- }
-
- boolean checkCacheSize(int size) {
- synchronized (mEventCache) {
- int cacheSize = mEventCache.size();
- if (cacheSize != size) {
- Log.d(TAG, "id = " + mWaitId + ": event cache size = " + cacheSize);
- for (int i = 0; i < cacheSize; i++) {
- EventData e = mEventCache.get(i);
- String sname = (e.mInfo != null) ? "(" + e.mInfo.getServiceName() + ")" : "";
- Log.d(TAG, "eventName is " + e.mCallbackName + sname);
- }
- }
- return (cacheSize == size);
- }
- }
-
- boolean checkForAdditionalEvents() {
- try {
- EventData e = waitForNewEvents();
- if (e != null) {
- String sname = (e.mInfo != null) ? "(" + e.mInfo.getServiceName() + ")" : "";
- Log.d(TAG, "ignoring unexpected event " + e.mCallbackName + sname);
- }
- return (e == null);
- }
- catch (InterruptedException ex) {
- return false;
- }
- }
-}
-
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
new file mode 100644
index 0000000..9307c27
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.cts
+
+import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted
+import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped
+import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.ServiceFound
+import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.ServiceLost
+import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.StartDiscoveryFailed
+import android.net.cts.NsdManagerTest.NsdDiscoveryRecord.DiscoveryEvent.StopDiscoveryFailed
+import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.RegistrationFailed
+import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.ServiceRegistered
+import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.ServiceUnregistered
+import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.UnregistrationFailed
+import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.ResolveFailed
+import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.ServiceResolved
+import android.net.nsd.NsdManager
+import android.net.nsd.NsdManager.DiscoveryListener
+import android.net.nsd.NsdManager.RegistrationListener
+import android.net.nsd.NsdManager.ResolveListener
+import android.net.nsd.NsdServiceInfo
+import android.platform.test.annotations.AppModeFull
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.android.net.module.util.ArrayTrackRecord
+import com.android.net.module.util.TrackRecord
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.net.ServerSocket
+import java.nio.charset.StandardCharsets
+import java.util.Random
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
+
+private const val TAG = "NsdManagerTest"
+private const val SERVICE_TYPE = "_nmt._tcp"
+private const val TIMEOUT_MS = 2000L
+private const val DBG = false
+
+@AppModeFull(reason = "Socket cannot bind in instant app mode")
+@RunWith(AndroidJUnit4::class)
+class NsdManagerTest {
+ private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+ private val nsdManager by lazy { context.getSystemService(NsdManager::class.java) }
+ private val serviceName = "NsdTest%04d".format(Random().nextInt(1000))
+
+ private interface NsdEvent
+ private open class NsdRecord<T : NsdEvent> private constructor(
+ private val history: ArrayTrackRecord<T>
+ ) : TrackRecord<T> by history {
+ constructor() : this(ArrayTrackRecord())
+
+ val nextEvents = history.newReadHead()
+
+ inline fun <reified V : NsdEvent> expectCallbackEventually(
+ crossinline predicate: (V) -> Boolean = { true }
+ ): V = nextEvents.poll(TIMEOUT_MS) { e -> e is V && predicate(e) } as V?
+ ?: fail("Callback for ${V::class.java.simpleName} not seen after $TIMEOUT_MS ms")
+
+ inline fun <reified V : NsdEvent> expectCallback(): V {
+ val nextEvent = nextEvents.poll(TIMEOUT_MS)
+ assertNotNull(nextEvent, "No callback received after $TIMEOUT_MS ms")
+ assertTrue(nextEvent is V, "Expected ${V::class.java.simpleName} but got " +
+ nextEvent.javaClass.simpleName)
+ return nextEvent
+ }
+ }
+
+ private class NsdRegistrationRecord : RegistrationListener,
+ NsdRecord<NsdRegistrationRecord.RegistrationEvent>() {
+ sealed class RegistrationEvent : NsdEvent {
+ abstract val serviceInfo: NsdServiceInfo
+
+ data class RegistrationFailed(
+ override val serviceInfo: NsdServiceInfo,
+ val errorCode: Int
+ ) : RegistrationEvent()
+
+ data class UnregistrationFailed(
+ override val serviceInfo: NsdServiceInfo,
+ val errorCode: Int
+ ) : RegistrationEvent()
+
+ data class ServiceRegistered(override val serviceInfo: NsdServiceInfo)
+ : RegistrationEvent()
+ data class ServiceUnregistered(override val serviceInfo: NsdServiceInfo)
+ : RegistrationEvent()
+ }
+
+ override fun onRegistrationFailed(si: NsdServiceInfo, err: Int) {
+ add(RegistrationFailed(si, err))
+ }
+
+ override fun onUnregistrationFailed(si: NsdServiceInfo, err: Int) {
+ add(UnregistrationFailed(si, err))
+ }
+
+ override fun onServiceRegistered(si: NsdServiceInfo) {
+ add(ServiceRegistered(si))
+ }
+
+ override fun onServiceUnregistered(si: NsdServiceInfo) {
+ add(ServiceUnregistered(si))
+ }
+ }
+
+ private class NsdDiscoveryRecord : DiscoveryListener,
+ NsdRecord<NsdDiscoveryRecord.DiscoveryEvent>() {
+ sealed class DiscoveryEvent : NsdEvent {
+ data class StartDiscoveryFailed(val serviceType: String, val errorCode: Int)
+ : DiscoveryEvent()
+
+ data class StopDiscoveryFailed(val serviceType: String, val errorCode: Int)
+ : DiscoveryEvent()
+
+ data class DiscoveryStarted(val serviceType: String) : DiscoveryEvent()
+ data class DiscoveryStopped(val serviceType: String) : DiscoveryEvent()
+ data class ServiceFound(val serviceInfo: NsdServiceInfo) : DiscoveryEvent()
+ data class ServiceLost(val serviceInfo: NsdServiceInfo) : DiscoveryEvent()
+ }
+
+ override fun onStartDiscoveryFailed(serviceType: String, err: Int) {
+ add(StartDiscoveryFailed(serviceType, err))
+ }
+
+ override fun onStopDiscoveryFailed(serviceType: String, err: Int) {
+ add(StopDiscoveryFailed(serviceType, err))
+ }
+
+ override fun onDiscoveryStarted(serviceType: String) {
+ add(DiscoveryStarted(serviceType))
+ }
+
+ override fun onDiscoveryStopped(serviceType: String) {
+ add(DiscoveryStopped(serviceType))
+ }
+
+ override fun onServiceFound(si: NsdServiceInfo) {
+ add(ServiceFound(si))
+ }
+
+ override fun onServiceLost(si: NsdServiceInfo) {
+ add(ServiceLost(si))
+ }
+
+ fun waitForServiceDiscovered(serviceName: String): NsdServiceInfo {
+ return expectCallbackEventually<ServiceFound> {
+ it.serviceInfo.serviceName == serviceName
+ }.serviceInfo
+ }
+ }
+
+ private class NsdResolveRecord : ResolveListener,
+ NsdRecord<NsdResolveRecord.ResolveEvent>() {
+ sealed class ResolveEvent : NsdEvent {
+ data class ResolveFailed(val serviceInfo: NsdServiceInfo, val errorCode: Int)
+ : ResolveEvent()
+
+ data class ServiceResolved(val serviceInfo: NsdServiceInfo) : ResolveEvent()
+ }
+
+ override fun onResolveFailed(si: NsdServiceInfo, err: Int) {
+ add(ResolveFailed(si, err))
+ }
+
+ override fun onServiceResolved(si: NsdServiceInfo) {
+ add(ServiceResolved(si))
+ }
+ }
+
+ @Test
+ fun testNsdManager() {
+ val si = NsdServiceInfo()
+ si.serviceType = SERVICE_TYPE
+ si.serviceName = serviceName
+ // Test binary data with various bytes
+ val testByteArray = byteArrayOf(-128, 127, 2, 1, 0, 1, 2)
+ // Test string data with 256 characters (25 blocks of 10 characters + 6)
+ val string256 = "1_________2_________3_________4_________5_________6_________" +
+ "7_________8_________9_________10________11________12________13________" +
+ "14________15________16________17________18________19________20________" +
+ "21________22________23________24________25________123456"
+
+ // Illegal attributes
+ listOf(
+ Triple(null, null, "null key"),
+ Triple("", null, "empty key"),
+ Triple(string256, null, "key with 256 characters"),
+ Triple("key", string256.substring(3),
+ "key+value combination with more than 255 characters"),
+ Triple("key", string256.substring(4), "key+value combination with 255 characters"),
+ Triple("\u0019", null, "key with invalid character"),
+ Triple("=", null, "key with invalid character"),
+ Triple("\u007f", null, "key with invalid character")
+ ).forEach {
+ assertFailsWith<IllegalArgumentException>(
+ "Setting invalid ${it.third} unexpectedly succeeded") {
+ si.setAttribute(it.first, it.second)
+ }
+ }
+
+ // Allowed attributes
+ si.setAttribute("booleanAttr", null as String?)
+ si.setAttribute("keyValueAttr", "value")
+ si.setAttribute("keyEqualsAttr", "=")
+ si.setAttribute(" whiteSpaceKeyValueAttr ", " value ")
+ si.setAttribute("binaryDataAttr", testByteArray)
+ si.setAttribute("nullBinaryDataAttr", null as ByteArray?)
+ si.setAttribute("emptyBinaryDataAttr", byteArrayOf())
+ si.setAttribute("longkey", string256.substring(9))
+ val socket = ServerSocket(0)
+ val localPort = socket.localPort
+ si.port = localPort
+ if (DBG) Log.d(TAG, "Port = $localPort")
+
+ val registrationRecord = NsdRegistrationRecord()
+ val registeredInfo = registerService(registrationRecord, si)
+
+ val discoveryRecord = NsdDiscoveryRecord()
+ nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
+
+ // Expect discovery started
+ discoveryRecord.expectCallback<DiscoveryStarted>()
+
+ // Expect a service record to be discovered
+ val foundInfo = discoveryRecord.waitForServiceDiscovered(registeredInfo.serviceName)
+
+ val resolvedService = resolveService(foundInfo)
+
+ // Check Txt attributes
+ assertEquals(8, resolvedService.attributes.size)
+ assertTrue(resolvedService.attributes.containsKey("booleanAttr"))
+ assertNull(resolvedService.attributes["booleanAttr"])
+ assertEquals("value", resolvedService.attributes["keyValueAttr"].utf8ToString())
+ assertEquals("=", resolvedService.attributes["keyEqualsAttr"].utf8ToString())
+ assertEquals(" value ",
+ resolvedService.attributes[" whiteSpaceKeyValueAttr "].utf8ToString())
+ assertEquals(string256.substring(9), resolvedService.attributes["longkey"].utf8ToString())
+ assertArrayEquals(testByteArray, resolvedService.attributes["binaryDataAttr"])
+ assertTrue(resolvedService.attributes.containsKey("nullBinaryDataAttr"))
+ assertNull(resolvedService.attributes["nullBinaryDataAttr"])
+ assertTrue(resolvedService.attributes.containsKey("emptyBinaryDataAttr"))
+ assertNull(resolvedService.attributes["emptyBinaryDataAttr"])
+ assertEquals(localPort, resolvedService.port)
+
+ // Unregister the service
+ nsdManager.unregisterService(registrationRecord)
+ registrationRecord.expectCallback<ServiceUnregistered>()
+
+ // Expect a callback for service lost
+ discoveryRecord.expectCallbackEventually<ServiceLost> {
+ it.serviceInfo.serviceName == serviceName
+ }
+
+ // Register service again to see if NsdManager can discover it
+ val si2 = NsdServiceInfo()
+ si2.serviceType = SERVICE_TYPE
+ si2.serviceName = serviceName
+ si2.port = localPort
+ val registrationRecord2 = NsdRegistrationRecord()
+ val registeredInfo2 = registerService(registrationRecord2, si2)
+
+ // Expect a service record to be discovered (and filter the ones
+ // that are unrelated to this test)
+ val foundInfo2 = discoveryRecord.waitForServiceDiscovered(registeredInfo2.serviceName)
+
+ // Resolve the service
+ val resolvedService2 = resolveService(foundInfo2)
+
+ // Check that the resolved service doesn't have any TXT records
+ assertEquals(0, resolvedService2.attributes.size)
+
+ nsdManager.stopServiceDiscovery(discoveryRecord)
+
+ discoveryRecord.expectCallbackEventually<DiscoveryStopped>()
+
+ nsdManager.unregisterService(registrationRecord2)
+ registrationRecord2.expectCallback<ServiceUnregistered>()
+ }
+
+ /**
+ * Register a service and return its registration record.
+ */
+ private fun registerService(record: NsdRegistrationRecord, si: NsdServiceInfo): NsdServiceInfo {
+ nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, record)
+ // We may not always get the name that we tried to register;
+ // This events tells us the name that was registered.
+ val cb = record.expectCallback<ServiceRegistered>()
+ return cb.serviceInfo
+ }
+
+ private fun resolveService(discoveredInfo: NsdServiceInfo): NsdServiceInfo {
+ val record = NsdResolveRecord()
+ nsdManager.resolveService(discoveredInfo, record)
+ val resolvedCb = record.expectCallback<ServiceResolved>()
+ assertEquals(discoveredInfo.serviceName, resolvedCb.serviceInfo.serviceName)
+
+ return resolvedCb.serviceInfo
+ }
+}
+
+private fun ByteArray?.utf8ToString(): String {
+ if (this == null) return ""
+ return String(this, StandardCharsets.UTF_8)
+}
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index fd0cd18..7254319 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -16,15 +16,11 @@
package android.net.cts.util;
-import static android.Manifest.permission.ACCESS_WIFI_STATE;
-import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
-import static android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION;
import static com.android.compatibility.common.util.PropertyUtil.getFirstApiLevel;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -48,15 +44,12 @@
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.net.TestNetworkManager;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
import android.os.ConditionVariable;
import android.os.IBinder;
-import android.os.SystemClock;
import android.system.Os;
import android.system.OsConstants;
import android.text.TextUtils;
@@ -64,23 +57,17 @@
import com.android.compatibility.common.util.SystemUtil;
import com.android.net.module.util.ConnectivitySettingsUtils;
-
-import junit.framework.AssertionFailedError;
+import com.android.testutils.ConnectUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
public final class CtsNetUtils {
private static final String TAG = CtsNetUtils.class.getSimpleName();
@@ -89,13 +76,7 @@
private static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 10_000;
private static final int CONNECTIVITY_CHANGE_TIMEOUT_SECS = 30;
- private static final int MAX_WIFI_CONNECT_RETRIES = 10;
- private static final int WIFI_CONNECT_INTERVAL_MS = 500;
- // Constants used by WifiManager.ActionListener#onFailure. Although onFailure is SystemApi,
- // the error code constants are not (they probably should be ?)
- private static final int WIFI_ERROR_IN_PROGRESS = 1;
- private static final int WIFI_ERROR_BUSY = 2;
private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
private static final String PRIVATE_DNS_MODE_STRICT = "hostname";
public static final int HTTP_PORT = 80;
@@ -237,10 +218,6 @@
* @return The network that was newly connected.
*/
private Network connectToWifi(boolean expectLegacyBroadcast) {
- final TestNetworkCallback callback = new TestNetworkCallback();
- mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
- Network wifiNetwork = null;
-
ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
IntentFilter filter = new IntentFilter();
@@ -248,159 +225,17 @@
mContext.registerReceiver(receiver, filter);
try {
- // Clear the wifi config blocklist (not the BSSID blocklist)
- clearWifiBlocklist();
- SystemUtil.runShellCommand("svc wifi enable");
- final WifiConfiguration config = getOrCreateWifiConfiguration();
- connectToWifiConfig(config);
-
- // Ensure we get an onAvailable callback and possibly a CONNECTIVITY_ACTION.
- wifiNetwork = callback.waitForAvailable();
- assertNotNull("onAvailable callback not received after connecting to " + config.SSID,
- wifiNetwork);
+ final Network network = new ConnectUtil(mContext).ensureWifiConnected();
if (expectLegacyBroadcast) {
- assertTrue("CONNECTIVITY_ACTION not received after connecting to " + config.SSID,
+ assertTrue("CONNECTIVITY_ACTION not received after connecting to " + network,
receiver.waitForState());
}
+ return network;
} catch (InterruptedException ex) {
- fail("connectToWifi was interrupted");
+ throw new AssertionError("connectToWifi was interrupted", ex);
} finally {
- mCm.unregisterNetworkCallback(callback);
mContext.unregisterReceiver(receiver);
}
-
- return wifiNetwork;
- }
-
- private void connectToWifiConfig(WifiConfiguration config) {
- for (int i = 0; i < MAX_WIFI_CONNECT_RETRIES; i++) {
- final Integer error = runAsShell(NETWORK_SETTINGS, () -> {
- final ConnectWifiListener listener = new ConnectWifiListener();
- mWifiManager.connect(config, listener);
- return listener.connectFuture.get(
- CONNECTIVITY_CHANGE_TIMEOUT_SECS, TimeUnit.SECONDS);
- });
-
- if (error == null) return;
-
- // Only retry for IN_PROGRESS and BUSY
- if (error != WIFI_ERROR_IN_PROGRESS && error != WIFI_ERROR_BUSY) {
- fail("Failed to connect to " + config.SSID + ": " + error);
- }
-
- Log.w(TAG, "connect failed with " + error + "; waiting before retry");
- SystemClock.sleep(WIFI_CONNECT_INTERVAL_MS);
- }
-
- fail("Failed to connect to " + config.SSID
- + " after " + MAX_WIFI_CONNECT_RETRIES + "retries");
- }
-
- private static class ConnectWifiListener implements WifiManager.ActionListener {
- /**
- * Future completed when the connect process ends. Provides the error code or null if none.
- */
- final CompletableFuture<Integer> connectFuture = new CompletableFuture<>();
- @Override
- public void onSuccess() {
- connectFuture.complete(null);
- }
-
- @Override
- public void onFailure(int reason) {
- connectFuture.complete(reason);
- }
- }
-
- private WifiConfiguration getOrCreateWifiConfiguration() {
- final List<WifiConfiguration> configs = runAsShell(NETWORK_SETTINGS,
- mWifiManager::getConfiguredNetworks);
- // If no network is configured, add a config for virtual access points if applicable
- if (configs.size() == 0) {
- final List<ScanResult> scanResults = getWifiScanResults();
- final WifiConfiguration virtualConfig = maybeConfigureVirtualNetwork(scanResults);
- assertNotNull("The device has no configured wifi network", virtualConfig);
-
- return virtualConfig;
- }
- // No need to add a configuration: there is already one.
- if (configs.size() > 1) {
- // For convenience in case of local testing on devices with multiple saved configs,
- // prefer the first configuration that is in range.
- // In actual tests, there should only be one configuration, and it should be usable as
- // assumed by WifiManagerTest.testConnect.
- Log.w(TAG, "Multiple wifi configurations found: "
- + configs.stream().map(c -> c.SSID).collect(Collectors.joining(", ")));
- final List<ScanResult> scanResultsList = getWifiScanResults();
- Log.i(TAG, "Scan results: " + scanResultsList.stream().map(c ->
- c.SSID + " (" + c.level + ")").collect(Collectors.joining(", ")));
- final Set<String> scanResults = scanResultsList.stream().map(
- s -> "\"" + s.SSID + "\"").collect(Collectors.toSet());
-
- return configs.stream().filter(c -> scanResults.contains(c.SSID))
- .findFirst().orElse(configs.get(0));
- }
- return configs.get(0);
- }
-
- private List<ScanResult> getWifiScanResults() {
- final CompletableFuture<List<ScanResult>> scanResultsFuture = new CompletableFuture<>();
- runAsShell(NETWORK_SETTINGS, () -> {
- final BroadcastReceiver receiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- scanResultsFuture.complete(mWifiManager.getScanResults());
- }
- };
- mContext.registerReceiver(receiver, new IntentFilter(SCAN_RESULTS_AVAILABLE_ACTION));
- mWifiManager.startScan();
- });
-
- try {
- return scanResultsFuture.get(CONNECTIVITY_CHANGE_TIMEOUT_SECS, TimeUnit.SECONDS);
- } catch (ExecutionException | InterruptedException | TimeoutException e) {
- throw new AssertionFailedError("Wifi scan results not received within timeout");
- }
- }
-
- /**
- * If a virtual wifi network is detected, add a configuration for that network.
- * TODO(b/158150376): have the test infrastructure add virtual wifi networks when appropriate.
- */
- private WifiConfiguration maybeConfigureVirtualNetwork(List<ScanResult> scanResults) {
- // Virtual wifi networks used on the emulator and cloud testing infrastructure
- final List<String> virtualSsids = Arrays.asList("VirtWifi", "AndroidWifi");
- Log.d(TAG, "Wifi scan results: " + scanResults);
- final ScanResult virtualScanResult = scanResults.stream().filter(
- s -> virtualSsids.contains(s.SSID)).findFirst().orElse(null);
-
- // Only add the virtual configuration if the virtual AP is detected in scans
- if (virtualScanResult == null) return null;
-
- final WifiConfiguration virtualConfig = new WifiConfiguration();
- // ASCII SSIDs need to be surrounded by double quotes
- virtualConfig.SSID = "\"" + virtualScanResult.SSID + "\"";
- virtualConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
-
- runAsShell(NETWORK_SETTINGS, () -> {
- final int networkId = mWifiManager.addNetwork(virtualConfig);
- assertTrue(networkId >= 0);
- assertTrue(mWifiManager.enableNetwork(networkId, false /* attemptConnect */));
- });
- return virtualConfig;
- }
-
- /**
- * Re-enable wifi networks that were blocklisted, typically because no internet connection was
- * detected the last time they were connected. This is necessary to make sure wifi can reconnect
- * to them.
- */
- private void clearWifiBlocklist() {
- runAsShell(NETWORK_SETTINGS, ACCESS_WIFI_STATE, () -> {
- for (WifiConfiguration cfg : mWifiManager.getConfiguredNetworks()) {
- assertTrue(mWifiManager.enableNetwork(cfg.networkId, false /* attemptConnect */));
- }
- });
}
/**
@@ -471,14 +306,18 @@
}
try {
+ if (wasWifiConnected) {
+ // Make sure the callback is registered before turning off WiFi.
+ callback.waitForAvailable();
+ }
SystemUtil.runShellCommand("svc wifi disable");
if (wasWifiConnected) {
// Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
assertNotNull("Did not receive onLost callback after disabling wifi",
callback.waitForLost());
- }
- if (wasWifiConnected && expectLegacyBroadcast) {
- assertTrue("Wifi failed to reach DISCONNECTED state.", receiver.waitForState());
+ if (expectLegacyBroadcast) {
+ assertTrue("Wifi failed to reach DISCONNECTED state.", receiver.waitForState());
+ }
}
} catch (InterruptedException ex) {
fail("disconnectFromWifi was interrupted");
@@ -541,6 +380,12 @@
return mCellNetworkCallback != null;
}
+ public void tearDown() {
+ if (cellConnectAttempted()) {
+ disconnectFromCell();
+ }
+ }
+
private NetworkRequest makeWifiNetworkRequest() {
return new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
@@ -582,6 +427,9 @@
}
if (mOldPrivateDnsMode != ConnectivitySettingsUtils.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) {
+ // Also restore hostname even if the value is not used since private dns is not in
+ // the strict mode to prevent setting being changed after test.
+ ConnectivitySettingsUtils.setPrivateDnsHostname(mContext, mOldPrivateDnsSpecifier);
ConnectivitySettingsUtils.setPrivateDnsMode(mContext, mOldPrivateDnsMode);
return;
}
@@ -634,6 +482,7 @@
NetworkCallback callback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
+ Log.i(TAG, "Link properties of network " + n + " changed to " + lp);
if (requiresValidatedServer && lp.getValidatedPrivateDnsServers().isEmpty()) {
return;
}
@@ -761,12 +610,14 @@
@Override
public void onAvailable(Network network) {
+ Log.i(TAG, "CtsNetUtils TestNetworkCallback onAvailable " + network);
currentNetwork = network;
mAvailableCv.open();
}
@Override
public void onLost(Network network) {
+ Log.i(TAG, "CtsNetUtils TestNetworkCallback onLost " + network);
lastLostNetwork = network;
if (network.equals(currentNetwork)) {
mAvailableCv.close();
diff --git a/tests/deflake/Android.bp b/tests/deflake/Android.bp
index 806f805..8205f1c 100644
--- a/tests/deflake/Android.bp
+++ b/tests/deflake/Android.bp
@@ -19,8 +19,17 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// FrameworksNetDeflakeTest depends on FrameworksNetTests so it should be disabled
+// if FrameworksNetTests is disabled.
+enable_frameworks_net_deflake_test = true
+// Placeholder
+// This is a placeholder comment to minimize merge conflicts, as enable_frameworks_net_deflake_test
+// may have different values depending on the branch
+// Placeholder
+
java_test_host {
name: "FrameworksNetDeflakeTest",
+ enabled: enable_frameworks_net_deflake_test,
srcs: ["src/**/*.kt"],
libs: [
"junit",
@@ -32,4 +41,14 @@
],
data: [":FrameworksNetTests"],
test_suites: ["device-tests"],
+ // It will get build error if just set enabled to true. It fails with "windows_common"
+ // depends on some disabled modules that are used by this test and it looks like set
+ // enable_frameworks_net_deflake_test to true also enables "windows" variant. Thus,
+ // disable this on target windows.
+ // TODO: Remove this when b/201754360 is fixed.
+ target: {
+ windows: {
+ enabled: false,
+ },
+ },
}
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp
index 2bc985a..7b5b44f 100644
--- a/tests/integration/Android.bp
+++ b/tests/integration/Android.bp
@@ -21,6 +21,7 @@
android_test {
name: "FrameworksNetIntegrationTests",
+ defaults: ["framework-connectivity-test-defaults"],
platform_apis: true,
certificate: "platform",
srcs: [
@@ -59,6 +60,7 @@
// Utilities for testing framework code both in integration and unit tests.
java_library {
name: "frameworks-net-integration-testutils",
+ defaults: ["framework-connectivity-test-defaults"],
srcs: ["util/**/*.java", "util/**/*.kt"],
static_libs: [
"androidx.annotation_annotation",
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 970b7d2..4dc86ff 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -27,6 +27,7 @@
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -310,6 +311,10 @@
assertTrue(mDisconnected.block(timeoutMs));
}
+ public void assertNotDisconnected(long timeoutMs) {
+ assertFalse(mDisconnected.block(timeoutMs));
+ }
+
public void sendLinkProperties(LinkProperties lp) {
mNetworkAgent.sendLinkProperties(lp);
}
diff --git a/tests/mts/Android.bp b/tests/mts/Android.bp
new file mode 100644
index 0000000..2c44010
--- /dev/null
+++ b/tests/mts/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "bpf_existence_test",
+ test_suites: [
+ "general-tests",
+ "mts-tethering",
+ ],
+ defaults: [
+ "connectivity-mainline-presubmit-cc-defaults",
+ ],
+ require_root: true,
+ static_libs: [
+ "libbase",
+ "libmodules-utils-build",
+ ],
+ srcs: [
+ "bpf_existence_test.cpp",
+ ],
+ compile_multilib: "first",
+ min_sdk_version: "29", // Ensure test runs on Q and above.
+}
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
new file mode 100644
index 0000000..142e013
--- /dev/null
+++ b/tests/mts/bpf_existence_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_existence_test.cpp - checks that the device has expected BPF programs and maps
+ */
+
+#include <cstdint>
+#include <set>
+#include <string>
+
+#include <android/api-level.h>
+#include <android-base/properties.h>
+#include <android-modules-utils/sdk_level.h>
+
+#include <gtest/gtest.h>
+
+using std::find;
+using std::set;
+using std::string;
+
+using android::modules::sdklevel::IsAtLeastR;
+using android::modules::sdklevel::IsAtLeastS;
+using android::modules::sdklevel::IsAtLeastT;
+
+// Mainline development branches lack the constant for the current development OS.
+#ifndef __ANDROID_API_T__
+#define __ANDROID_API_T__ 33
+#endif
+
+#define PLATFORM "/sys/fs/bpf/"
+#define TETHERING "/sys/fs/bpf/tethering/"
+
+class BpfExistenceTest : public ::testing::Test {
+};
+
+static const set<string> INTRODUCED_R = {
+ PLATFORM "map_offload_tether_ingress_map",
+ PLATFORM "map_offload_tether_limit_map",
+ PLATFORM "map_offload_tether_stats_map",
+ PLATFORM "prog_offload_schedcls_ingress_tether_ether",
+ PLATFORM "prog_offload_schedcls_ingress_tether_rawip",
+};
+
+static const set<string> INTRODUCED_S = {
+ TETHERING "map_offload_tether_dev_map",
+ TETHERING "map_offload_tether_downstream4_map",
+ TETHERING "map_offload_tether_downstream64_map",
+ TETHERING "map_offload_tether_downstream6_map",
+ TETHERING "map_offload_tether_error_map",
+ TETHERING "map_offload_tether_limit_map",
+ TETHERING "map_offload_tether_stats_map",
+ TETHERING "map_offload_tether_upstream4_map",
+ TETHERING "map_offload_tether_upstream6_map",
+ TETHERING "map_test_tether_downstream6_map",
+ TETHERING "prog_offload_schedcls_tether_downstream4_ether",
+ TETHERING "prog_offload_schedcls_tether_downstream4_rawip",
+ TETHERING "prog_offload_schedcls_tether_downstream6_ether",
+ TETHERING "prog_offload_schedcls_tether_downstream6_rawip",
+ TETHERING "prog_offload_schedcls_tether_upstream4_ether",
+ TETHERING "prog_offload_schedcls_tether_upstream4_rawip",
+ TETHERING "prog_offload_schedcls_tether_upstream6_ether",
+ TETHERING "prog_offload_schedcls_tether_upstream6_rawip",
+};
+
+static const set<string> REMOVED_S = {
+ PLATFORM "map_offload_tether_ingress_map",
+ PLATFORM "map_offload_tether_limit_map",
+ PLATFORM "map_offload_tether_stats_map",
+ PLATFORM "prog_offload_schedcls_ingress_tether_ether",
+ PLATFORM "prog_offload_schedcls_ingress_tether_rawip",
+};
+
+static const set<string> INTRODUCED_T = {
+};
+
+static const set<string> REMOVED_T = {
+};
+
+void addAll(set<string>* a, const set<string>& b) {
+ a->insert(b.begin(), b.end());
+}
+
+void removeAll(set<string>* a, const set<string>& b) {
+ for (const auto& toRemove : b) {
+ a->erase(toRemove);
+ }
+}
+
+void getFileLists(set<string>* expected, set<string>* unexpected) {
+ unexpected->clear();
+ expected->clear();
+
+ addAll(unexpected, INTRODUCED_R);
+ addAll(unexpected, INTRODUCED_S);
+ addAll(unexpected, INTRODUCED_T);
+
+ if (IsAtLeastR()) {
+ addAll(expected, INTRODUCED_R);
+ removeAll(unexpected, INTRODUCED_R);
+ // Nothing removed in R.
+ }
+
+ if (IsAtLeastS()) {
+ addAll(expected, INTRODUCED_S);
+ removeAll(expected, REMOVED_S);
+
+ addAll(unexpected, REMOVED_S);
+ removeAll(unexpected, INTRODUCED_S);
+ }
+
+ // Nothing added or removed in SCv2.
+
+ if (IsAtLeastT()) {
+ addAll(expected, INTRODUCED_T);
+ removeAll(expected, REMOVED_T);
+
+ addAll(unexpected, REMOVED_T);
+ removeAll(unexpected, INTRODUCED_T);
+ }
+}
+
+void checkFiles() {
+ set<string> mustExist;
+ set<string> mustNotExist;
+
+ getFileLists(&mustExist, &mustNotExist);
+
+ for (const auto& file : mustExist) {
+ EXPECT_EQ(0, access(file.c_str(), R_OK)) << file << " does not exist";
+ }
+ for (const auto& file : mustNotExist) {
+ int ret = access(file.c_str(), R_OK);
+ int err = errno;
+ EXPECT_EQ(-1, ret) << file << " unexpectedly exists";
+ if (ret == -1) {
+ EXPECT_EQ(ENOENT, err) << " accessing " << file << " failed with errno " << err;
+ }
+ }
+}
+
+TEST_F(BpfExistenceTest, TestPrograms) {
+ // Pre-flight check to ensure test has been updated.
+ uint64_t buildVersionSdk = android_get_device_api_level();
+ ASSERT_NE(0, buildVersionSdk) << "Unable to determine device SDK version";
+ if (buildVersionSdk > __ANDROID_API_T__ && buildVersionSdk != __ANDROID_API_FUTURE__) {
+ FAIL() << "Unknown OS version " << buildVersionSdk << ", please update this test";
+ }
+
+ // Only unconfined root is guaranteed to be able to access everything in /sys/fs/bpf.
+ ASSERT_EQ(0, getuid()) << "This test must run as root.";
+
+ checkFiles();
+}
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 7b23255..48751f4 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -3,16 +3,29 @@
//########################################################################
package {
// See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "Android-Apache-2.0"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// Whether to enable the FrameworksNetTests. Set to false in the branches that might have older
+// frameworks/base since FrameworksNetTests includes the test for classes that are not in
+// connectivity module.
+enable_frameworks_net_tests = true
+// Placeholder
+// This is a placeholder comment to minimize merge conflicts, as enable_frameworks_net_tests
+// may have different values depending on the branch
+// Placeholder
+
java_defaults {
name: "FrameworksNetTests-jni-defaults",
jni_libs: [
"ld-android",
"libbase",
"libbinder",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
"libc++",
"libcgrouprc",
@@ -44,6 +57,52 @@
],
}
+filegroup {
+ name: "non-connectivity-module-test",
+ srcs: [
+ "java/android/app/usage/*.java",
+ "java/android/net/Ikev2VpnProfileTest.java",
+ "java/android/net/IpMemoryStoreTest.java",
+ "java/android/net/IpSecAlgorithmTest.java",
+ "java/android/net/IpSecConfigTest.java",
+ "java/android/net/IpSecManagerTest.java",
+ "java/android/net/IpSecTransformTest.java",
+ "java/android/net/KeepalivePacketDataUtilTest.java",
+ "java/android/net/NetworkIdentityTest.kt",
+ "java/android/net/NetworkStats*.java",
+ "java/android/net/NetworkTemplateTest.kt",
+ "java/android/net/TelephonyNetworkSpecifierTest.java",
+ "java/android/net/VpnManagerTest.java",
+ "java/android/net/ipmemorystore/*.java",
+ "java/android/net/nsd/*.java",
+ "java/com/android/internal/net/NetworkUtilsInternalTest.java",
+ "java/com/android/internal/net/VpnProfileTest.java",
+ "java/com/android/server/IpSecServiceParameterizedTest.java",
+ "java/com/android/server/IpSecServiceRefcountedResourceTest.java",
+ "java/com/android/server/IpSecServiceTest.java",
+ "java/com/android/server/NetworkManagementServiceTest.java",
+ "java/com/android/server/NsdServiceTest.java",
+ "java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java",
+ "java/com/android/server/connectivity/IpConnectivityMetricsTest.java",
+ "java/com/android/server/connectivity/MultipathPolicyTrackerTest.java",
+ "java/com/android/server/connectivity/NetdEventListenerServiceTest.java",
+ "java/com/android/server/connectivity/VpnTest.java",
+ "java/com/android/server/net/ipmemorystore/*.java",
+ "java/com/android/server/net/NetworkStats*.java",
+ ]
+}
+
+// Subset of services-core used to by ConnectivityService tests to test VPN realistically.
+// This is stripped by jarjar (see rules below) from other unrelated classes, so tests do not
+// include most classes from services-core, which are unrelated and cause wrong code coverage
+// calculations.
+java_library {
+ name: "services.core-vpn",
+ static_libs: ["services.core"],
+ jarjar_rules: "vpn-jarjar-rules.txt",
+ visibility: ["//visibility:private"],
+}
+
android_library {
name: "FrameworksNetTestsLib",
min_sdk_version: "30",
@@ -54,6 +113,7 @@
"java/**/*.java",
"java/**/*.kt",
],
+ exclude_srcs: [":non-connectivity-module-test"],
jarjar_rules: "jarjar-rules.txt",
static_libs: [
"androidx.test.rules",
@@ -66,10 +126,12 @@
"framework-protos",
"mockito-target-minus-junit4",
"net-tests-utils",
+ "net-utils-services-common",
+ "platform-compat-test-rules",
"platform-test-annotations",
"service-connectivity-pre-jarjar",
- "services.core",
- "services.net",
+ "service-connectivity-tiramisu-pre-jarjar",
+ "services.core-vpn",
],
libs: [
"android.net.ipsec.ike.stubs.module_lib",
@@ -83,15 +145,24 @@
android_test {
name: "FrameworksNetTests",
+ enabled: enable_frameworks_net_tests,
min_sdk_version: "30",
defaults: [
"framework-connectivity-test-defaults",
"FrameworksNetTests-jni-defaults",
],
+ // this is in addition to FrameworksNetTestsLib.
+ srcs: [":non-connectivity-module-test"],
test_suites: ["device-tests"],
static_libs: [
+ "services.core",
+ "services.net",
"FrameworksNetTestsLib",
],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ ],
jni_libs: [
"libservice-connectivity",
]
diff --git a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
index 08a3007..561e621 100644
--- a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
+++ b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
@@ -220,6 +220,47 @@
TEST_SUBSCRIBER_ID));
}
+ @Test
+ public void testQueryTaggedSummary() throws Exception {
+ final long startTime = 1;
+ final long endTime = 100;
+
+ reset(mStatsSession);
+ when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
+ when(mStatsSession.getTaggedSummaryForAllUid(any(NetworkTemplate.class),
+ anyLong(), anyLong()))
+ .thenReturn(new android.net.NetworkStats(0, 0));
+ final NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+ .setMeteredness(NetworkStats.Bucket.METERED_YES).build();
+ NetworkStats stats = mManager.queryTaggedSummary(template, startTime, endTime);
+
+ verify(mStatsSession, times(1)).getTaggedSummaryForAllUid(
+ eq(template), eq(startTime), eq(endTime));
+
+ assertFalse(stats.hasNextBucket());
+ }
+
+
+ @Test
+ public void testQueryDetailsForDevice() throws Exception {
+ final long startTime = 1;
+ final long endTime = 100;
+
+ reset(mStatsSession);
+ when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
+ when(mStatsSession.getHistoryIntervalForNetwork(any(NetworkTemplate.class),
+ anyInt(), anyLong(), anyLong()))
+ .thenReturn(new NetworkStatsHistory(10, 0));
+ final NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+ .setMeteredness(NetworkStats.Bucket.METERED_YES).build();
+ NetworkStats stats = mManager.queryDetailsForDevice(template, startTime, endTime);
+
+ verify(mStatsSession, times(1)).getHistoryIntervalForNetwork(
+ eq(template), eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
+
+ assertFalse(stats.hasNextBucket());
+ }
+
private void assertBucketMatches(Entry expected, NetworkStats.Bucket actual) {
assertEquals(expected.uid, actual.getUid());
assertEquals(expected.rxBytes, actual.getRxBytes());
diff --git a/tests/unit/java/android/net/ConnectivityManagerTest.java b/tests/unit/java/android/net/ConnectivityManagerTest.java
index e7873af..f324630 100644
--- a/tests/unit/java/android/net/ConnectivityManagerTest.java
+++ b/tests/unit/java/android/net/ConnectivityManagerTest.java
@@ -37,6 +37,8 @@
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
+import static com.android.testutils.MiscAsserts.assertThrows;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -45,6 +47,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
@@ -83,6 +86,8 @@
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
public class ConnectivityManagerTest {
+ private static final int TIMEOUT_MS = 30_000;
+ private static final int SHORT_TIMEOUT_MS = 150;
@Mock Context mCtx;
@Mock IConnectivityManager mService;
@@ -231,7 +236,7 @@
// callback triggers
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
- verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
+ verify(callback, timeout(TIMEOUT_MS).times(1)).onAvailable(any(Network.class),
any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
@@ -240,7 +245,7 @@
// callback does not trigger anymore.
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_LOSING));
- verify(callback, timeout(500).times(0)).onLosing(any(), anyInt());
+ verify(callback, after(SHORT_TIMEOUT_MS).never()).onLosing(any(), anyInt());
}
@Test
@@ -260,7 +265,7 @@
// callback triggers
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
- verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
+ verify(callback, timeout(TIMEOUT_MS).times(1)).onAvailable(any(Network.class),
any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
@@ -269,7 +274,7 @@
// callback does not trigger anymore.
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_LOSING));
- verify(callback, timeout(100).times(0)).onLosing(any(), anyInt());
+ verify(callback, after(SHORT_TIMEOUT_MS).never()).onLosing(any(), anyInt());
// callback can be registered again
when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
@@ -278,7 +283,7 @@
// callback triggers
captor.getValue().send(makeMessage(req2, ConnectivityManager.CALLBACK_LOST));
- verify(callback, timeout(100).times(1)).onLost(any());
+ verify(callback, timeout(TIMEOUT_MS).times(1)).onLost(any());
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -314,6 +319,21 @@
}
@Test
+ public void testDefaultNetworkActiveListener() throws Exception {
+ final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
+ final ConnectivityManager.OnNetworkActiveListener listener =
+ mock(ConnectivityManager.OnNetworkActiveListener.class);
+ assertThrows(IllegalArgumentException.class,
+ () -> manager.removeDefaultNetworkActiveListener(listener));
+ manager.addDefaultNetworkActiveListener(listener);
+ verify(mService, times(1)).registerNetworkActivityListener(any());
+ manager.removeDefaultNetworkActiveListener(listener);
+ verify(mService, times(1)).unregisterNetworkActivityListener(any());
+ assertThrows(IllegalArgumentException.class,
+ () -> manager.removeDefaultNetworkActiveListener(listener));
+ }
+
+ @Test
public void testArgumentValidation() throws Exception {
ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
diff --git a/tests/unit/java/android/net/Ikev2VpnProfileTest.java b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
index 56e5c62..a151f03 100644
--- a/tests/unit/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
@@ -35,6 +35,7 @@
import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -64,6 +65,9 @@
private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
private static final int TEST_MTU = 1300;
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
private final MockContext mMockContext =
new MockContext() {
@Override
@@ -259,6 +263,28 @@
}
}
+
+ // TODO: Refer to Build.VERSION_CODES.SC_V2 when it's available in AOSP
+ @DevSdkIgnoreRule.IgnoreUpTo(32)
+ @Test
+ public void testBuildExcludeLocalRoutesSet() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAuthPsk(PSK_BYTES);
+ builder.setExcludeLocalRoutes(true);
+
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+ assertTrue(profile.getExcludeLocalRoutes());
+
+ builder.setBypassable(false);
+ try {
+ builder.build();
+ fail("Expected exception because excludeLocalRoutes should be set only"
+ + " on the bypassable VPN");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
@Test
public void testBuildInvalidMtu() throws Exception {
final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
diff --git a/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java b/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
index 8498b6f..6afa4e9 100644
--- a/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
+++ b/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
@@ -27,6 +27,7 @@
import android.os.Build;
import android.util.Log;
+import com.android.server.connectivity.TcpKeepaliveController;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -81,7 +82,7 @@
testInfo.tos = tos;
testInfo.ttl = ttl;
try {
- resultData = KeepalivePacketDataUtil.fromStableParcelable(testInfo);
+ resultData = TcpKeepaliveController.fromStableParcelable(testInfo);
} catch (InvalidPacketException e) {
fail("InvalidPacketException: " + e);
}
@@ -155,7 +156,7 @@
testInfo.ttl = ttl;
TcpKeepalivePacketData testData = null;
TcpKeepalivePacketDataParcelable resultData = null;
- testData = KeepalivePacketDataUtil.fromStableParcelable(testInfo);
+ testData = TcpKeepaliveController.fromStableParcelable(testInfo);
resultData = KeepalivePacketDataUtil.toStableParcelable(testData);
assertArrayEquals(resultData.srcAddress, IPV4_KEEPALIVE_SRC_ADDR);
assertArrayEquals(resultData.dstAddress, IPV4_KEEPALIVE_DST_ADDR);
@@ -198,11 +199,11 @@
testParcel.ttl = ttl;
final KeepalivePacketData testData =
- KeepalivePacketDataUtil.fromStableParcelable(testParcel);
+ TcpKeepaliveController.fromStableParcelable(testParcel);
final TcpKeepalivePacketDataParcelable parsedParcelable =
KeepalivePacketDataUtil.parseTcpKeepalivePacketData(testData);
final TcpKeepalivePacketData roundTripData =
- KeepalivePacketDataUtil.fromStableParcelable(parsedParcelable);
+ TcpKeepaliveController.fromStableParcelable(parsedParcelable);
// Generated packet is the same, but rcvWnd / wndScale will differ if scale is non-zero
assertTrue(testData.getPacket().length > 0);
@@ -210,11 +211,11 @@
testParcel.rcvWndScale = 0;
final KeepalivePacketData noScaleTestData =
- KeepalivePacketDataUtil.fromStableParcelable(testParcel);
+ TcpKeepaliveController.fromStableParcelable(testParcel);
final TcpKeepalivePacketDataParcelable noScaleParsedParcelable =
KeepalivePacketDataUtil.parseTcpKeepalivePacketData(noScaleTestData);
final TcpKeepalivePacketData noScaleRoundTripData =
- KeepalivePacketDataUtil.fromStableParcelable(noScaleParsedParcelable);
+ TcpKeepaliveController.fromStableParcelable(noScaleParsedParcelable);
assertEquals(noScaleTestData, noScaleRoundTripData);
assertTrue(noScaleTestData.getPacket().length > 0);
assertArrayEquals(noScaleTestData.getPacket(), noScaleRoundTripData.getPacket());
diff --git a/tests/unit/java/android/net/NetworkIdentityTest.kt b/tests/unit/java/android/net/NetworkIdentityTest.kt
index f963593..6ad8b06 100644
--- a/tests/unit/java/android/net/NetworkIdentityTest.kt
+++ b/tests/unit/java/android/net/NetworkIdentityTest.kt
@@ -16,20 +16,38 @@
package android.net
+import android.content.Context
+import android.net.ConnectivityManager.TYPE_MOBILE
import android.net.NetworkIdentity.OEM_NONE
import android.net.NetworkIdentity.OEM_PAID
import android.net.NetworkIdentity.OEM_PRIVATE
import android.net.NetworkIdentity.getOemBitfield
+import android.telephony.TelephonyManager
import android.os.Build
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+private const val TEST_IMSI = "testimsi"
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class NetworkIdentityTest {
+ private val mockContext = mock(Context::class.java)
+
+ private fun buildMobileNetworkStateSnapshot(
+ caps: NetworkCapabilities,
+ subscriberId: String
+ ): NetworkStateSnapshot {
+ return NetworkStateSnapshot(mock(Network::class.java), caps,
+ LinkProperties(), subscriberId, TYPE_MOBILE)
+ }
+
@Test
fun testGetOemBitfield() {
val oemNone = NetworkCapabilities().apply {
@@ -54,4 +72,60 @@
assertEquals(getOemBitfield(oemPrivate), OEM_PRIVATE)
assertEquals(getOemBitfield(oemAll), OEM_PAID or OEM_PRIVATE)
}
+
+ @Test
+ fun testIsMetered() {
+ // Verify network is metered.
+ val netIdent1 = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(NetworkCapabilities(), TEST_IMSI),
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ assertTrue(netIdent1.isMetered())
+
+ // Verify network is not metered because it has NET_CAPABILITY_NOT_METERED capability.
+ val capsNotMetered = NetworkCapabilities.Builder().apply {
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ }.build()
+ val netIdent2 = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(capsNotMetered, TEST_IMSI),
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ assertFalse(netIdent2.isMetered())
+
+ // Verify network is not metered because it has NET_CAPABILITY_TEMPORARILY_NOT_METERED
+ // capability .
+ val capsTempNotMetered = NetworkCapabilities().apply {
+ setCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED, true)
+ }
+ val netIdent3 = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(capsTempNotMetered, TEST_IMSI),
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ assertFalse(netIdent3.isMetered())
+ }
+
+ @Test
+ fun testBuilder() {
+ val oemPrivateRoamingNotMeteredCap = NetworkCapabilities().apply {
+ addCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ }
+ val identFromSnapshot = NetworkIdentity.Builder().setNetworkStateSnapshot(
+ buildMobileNetworkStateSnapshot(oemPrivateRoamingNotMeteredCap, TEST_IMSI))
+ .setDefaultNetwork(true)
+ .setRatType(TelephonyManager.NETWORK_TYPE_UMTS)
+ .build()
+ val identFromLegacyBuild = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(oemPrivateRoamingNotMeteredCap, TEST_IMSI),
+ true /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ val identFromConstructor = NetworkIdentity(TYPE_MOBILE,
+ TelephonyManager.NETWORK_TYPE_UMTS,
+ TEST_IMSI,
+ null /* wifiNetworkKey */,
+ true /* roaming */,
+ false /* metered */,
+ true /* defaultNetwork */,
+ NetworkTemplate.OEM_MANAGED_PRIVATE)
+ assertEquals(identFromLegacyBuild, identFromSnapshot)
+ assertEquals(identFromConstructor, identFromSnapshot)
+
+ // TODO: Add test cases for wifiNetworkKey and ratType.
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/unit/java/android/net/NetworkStatsAccessTest.java
similarity index 69%
rename from tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
rename to tests/unit/java/android/net/NetworkStatsAccessTest.java
index 03d9404..97a93ca 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/unit/java/android/net/NetworkStatsAccessTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,10 +11,12 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.server.net;
+package android.net;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
@@ -22,15 +24,13 @@
import android.Manifest;
import android.Manifest.permission;
import android.app.AppOpsManager;
-import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Build;
import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
-import com.android.server.LocalServices;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -43,123 +43,112 @@
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
public class NetworkStatsAccessTest {
private static final String TEST_PKG = "com.example.test";
+ private static final int TEST_PID = 1234;
private static final int TEST_UID = 12345;
@Mock private Context mContext;
- @Mock private DevicePolicyManagerInternal mDpmi;
+ @Mock private DevicePolicyManager mDpm;
@Mock private TelephonyManager mTm;
@Mock private AppOpsManager mAppOps;
// Hold the real service so we can restore it when tearing down the test.
- private DevicePolicyManagerInternal mSystemDpmi;
+ private DevicePolicyManager mSystemDpm;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mSystemDpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
- LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
- LocalServices.addService(DevicePolicyManagerInternal.class, mDpmi);
-
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTm);
when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOps);
+ when(mContext.getSystemServiceName(DevicePolicyManager.class))
+ .thenReturn(Context.DEVICE_POLICY_SERVICE);
+ when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(mDpm);
+
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ setHasNetworkStackPermission(false);
}
@After
public void tearDown() throws Exception {
- LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
- LocalServices.addService(DevicePolicyManagerInternal.class, mSystemDpmi);
}
@Test
public void testCheckAccessLevel_hasCarrierPrivileges() throws Exception {
setHasCarrierPrivileges(true);
- setIsDeviceOwner(false);
- setIsProfileOwner(false);
- setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
- setHasReadHistoryPermission(false);
assertEquals(NetworkStatsAccess.Level.DEVICE,
- NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
@Test
public void testCheckAccessLevel_isDeviceOwner() throws Exception {
- setHasCarrierPrivileges(false);
setIsDeviceOwner(true);
- setIsProfileOwner(false);
- setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
- setHasReadHistoryPermission(false);
assertEquals(NetworkStatsAccess.Level.DEVICE,
- NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
@Test
public void testCheckAccessLevel_isProfileOwner() throws Exception {
- setHasCarrierPrivileges(false);
- setIsDeviceOwner(false);
setIsProfileOwner(true);
- setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
- setHasReadHistoryPermission(false);
assertEquals(NetworkStatsAccess.Level.USER,
- NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
@Test
public void testCheckAccessLevel_hasAppOpsBitAllowed() throws Exception {
- setHasCarrierPrivileges(false);
- setIsDeviceOwner(false);
setIsProfileOwner(true);
setHasAppOpsPermission(AppOpsManager.MODE_ALLOWED, false);
- setHasReadHistoryPermission(false);
assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY,
- NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
@Test
public void testCheckAccessLevel_hasAppOpsBitDefault_grantedPermission() throws Exception {
- setHasCarrierPrivileges(false);
- setIsDeviceOwner(false);
setIsProfileOwner(true);
setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, true);
- setHasReadHistoryPermission(false);
assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY,
- NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
@Test
public void testCheckAccessLevel_hasReadHistoryPermission() throws Exception {
- setHasCarrierPrivileges(false);
- setIsDeviceOwner(false);
setIsProfileOwner(true);
- setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
setHasReadHistoryPermission(true);
assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY,
- NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
@Test
public void testCheckAccessLevel_deniedAppOpsBit() throws Exception {
- setHasCarrierPrivileges(false);
- setIsDeviceOwner(false);
- setIsProfileOwner(false);
setHasAppOpsPermission(AppOpsManager.MODE_ERRORED, true);
- setHasReadHistoryPermission(false);
assertEquals(NetworkStatsAccess.Level.DEFAULT,
- NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
@Test
public void testCheckAccessLevel_deniedAppOpsBit_deniedPermission() throws Exception {
- setHasCarrierPrivileges(false);
- setIsDeviceOwner(false);
- setIsProfileOwner(false);
- setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
- setHasReadHistoryPermission(false);
assertEquals(NetworkStatsAccess.Level.DEFAULT,
- NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
+ }
+
+ @Test
+ public void testCheckAccessLevel_hasNetworkStackPermission() throws Exception {
+ assertEquals(NetworkStatsAccess.Level.DEFAULT,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
+
+ setHasNetworkStackPermission(true);
+ assertEquals(NetworkStatsAccess.Level.DEVICE,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
+
+ setHasNetworkStackPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEFAULT,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
private void setHasCarrierPrivileges(boolean hasPrivileges) {
@@ -169,16 +158,16 @@
}
private void setIsDeviceOwner(boolean isOwner) {
- when(mDpmi.isActiveDeviceOwner(TEST_UID)).thenReturn(isOwner);
+ when(mDpm.isDeviceOwnerApp(TEST_PKG)).thenReturn(isOwner);
}
private void setIsProfileOwner(boolean isOwner) {
- when(mDpmi.isActiveProfileOwner(TEST_UID)).thenReturn(isOwner);
+ when(mDpm.isProfileOwnerApp(TEST_PKG)).thenReturn(isOwner);
}
private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) {
- when(mAppOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS, TEST_UID, TEST_PKG))
- .thenReturn(appOpsMode);
+ when(mAppOps.noteOp(AppOpsManager.OPSTR_GET_USAGE_STATS, TEST_UID, TEST_PKG,
+ null /* attributionTag */, null /* message */)).thenReturn(appOpsMode);
when(mContext.checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn(
hasPermission ? PackageManager.PERMISSION_GRANTED
: PackageManager.PERMISSION_DENIED);
@@ -189,4 +178,10 @@
.thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
: PackageManager.PERMISSION_DENIED);
}
+
+ private void setHasNetworkStackPermission(boolean hasPermission) {
+ when(mContext.checkPermission(android.Manifest.permission.NETWORK_STACK,
+ TEST_PID, TEST_UID)).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED);
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
similarity index 94%
rename from tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
rename to tests/unit/java/android/net/NetworkStatsCollectionTest.java
index e771558..2e82986 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.net;
+package android.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkIdentity.OEM_NONE;
@@ -28,7 +28,8 @@
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
+import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertArrayEquals;
@@ -37,12 +38,6 @@
import static org.junit.Assert.fail;
import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkIdentity;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.SubscriptionPlan;
@@ -64,6 +59,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -83,7 +79,7 @@
*/
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
public class NetworkStatsCollectionTest {
private static final String TEST_FILE = "test.bin";
@@ -98,14 +94,11 @@
@Before
public void setUp() throws Exception {
sOriginalClock = RecurrenceRule.sClock;
- // ignore any device overlay while testing
- NetworkTemplate.forceAllNetworkTypes();
}
@After
public void tearDown() throws Exception {
RecurrenceRule.sClock = sOriginalClock;
- NetworkTemplate.resetForceAllNetworkTypes();
}
private void setClock(Instant instant) {
@@ -123,7 +116,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
+ 636014522L, 709291L, 88037144L, 518820L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -137,7 +130,7 @@
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
+ 636014522L, 709291L, 88037144L, 518820L, NetworkStatsAccess.Level.DEVICE);
}
@Test
@@ -151,7 +144,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
+ 637073904L, 711398L, 88342093L, 521006L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -165,7 +158,7 @@
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
+ 637073904L, 711398L, 88342093L, 521006L, NetworkStatsAccess.Level.DEVICE);
}
@Test
@@ -203,8 +196,8 @@
// record empty data straddling between buckets
final NetworkStats.Entry entry = new NetworkStats.Entry();
entry.rxBytes = 32;
- collection.recordData(null, UID_ALL, SET_DEFAULT, TAG_NONE, 30 * MINUTE_IN_MILLIS,
- 90 * MINUTE_IN_MILLIS, entry);
+ collection.recordData(Mockito.mock(NetworkIdentitySet.class), UID_ALL, SET_DEFAULT,
+ TAG_NONE, 30 * MINUTE_IN_MILLIS, 90 * MINUTE_IN_MILLIS, entry);
// assert that we report boundary in atomic buckets
assertEquals(0, collection.getStartMillis());
@@ -253,8 +246,8 @@
collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
// Verify security check in getHistory.
- assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, myUid, SET_DEFAULT,
- TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid));
+ assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null,
+ myUid, SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid));
try {
collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, otherUidInSameUser,
SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid);
@@ -278,7 +271,8 @@
new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE);
stageFile(R.raw.netstats_v1, testFile);
- final NetworkStatsCollection emptyCollection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ final NetworkStatsCollection emptyCollection =
+ new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
collection.readLegacyNetwork(testFile);
@@ -316,7 +310,8 @@
assertEquals(0L, history.getTotalBytes());
// Normal collection should be untouched
- history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ history = getHistory(collection, plan, TIME_A, TIME_C);
+ i = 0;
assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -345,7 +340,8 @@
// Slice from middle should be untouched
history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
- TIME_B + HOUR_IN_MILLIS); i = 0;
+ TIME_B + HOUR_IN_MILLIS);
+ i = 0;
assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
@@ -368,7 +364,8 @@
assertEquals(200000L, history.getTotalBytes());
// Normal collection should be augmented
- history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ history = getHistory(collection, plan, TIME_A, TIME_C);
+ i = 0;
assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -400,7 +397,8 @@
// Slice from middle should be augmented
history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
- TIME_B + HOUR_IN_MILLIS); i = 0;
+ TIME_B + HOUR_IN_MILLIS);
+ i = 0;
assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
@@ -423,7 +421,8 @@
assertEquals(400000L, history.getTotalBytes());
// Normal collection should be augmented
- history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ history = getHistory(collection, plan, TIME_A, TIME_C);
+ i = 0;
assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -454,7 +453,8 @@
// Slice from middle should be augmented
history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
- TIME_B + HOUR_IN_MILLIS); i = 0;
+ TIME_B + HOUR_IN_MILLIS);
+ i = 0;
assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
diff --git a/tests/unit/java/android/net/NetworkStatsTest.java b/tests/unit/java/android/net/NetworkStatsTest.java
index c971da1..b0cc16c 100644
--- a/tests/unit/java/android/net/NetworkStatsTest.java
+++ b/tests/unit/java/android/net/NetworkStatsTest.java
@@ -37,6 +37,7 @@
import static android.net.NetworkStats.UID_ALL;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Build;
@@ -53,8 +54,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Iterator;
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
@@ -1037,6 +1040,29 @@
assertEquals(secondEntry, stats.getValues(1, null));
}
+ @Test
+ public void testIterator() {
+ final NetworkStats emptyStats = new NetworkStats(0, 0);
+ final Iterator emptyIterator = emptyStats.iterator();
+ assertFalse(emptyIterator.hasNext());
+
+ final int numEntries = 10;
+ final ArrayList<NetworkStats.Entry> entries = new ArrayList<>();
+ final NetworkStats stats = new NetworkStats(TEST_START, 1);
+ for (int i = 0; i < numEntries; ++i) {
+ NetworkStats.Entry entry = new NetworkStats.Entry("test1", 10100, SET_DEFAULT,
+ TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+ i * 10L /* rxBytes */, i * 3L /* rxPackets */,
+ i * 15L /* txBytes */, i * 2L /* txPackets */, 0L /* operations */);
+ stats.insertEntry(entry);
+ entries.add(entry);
+ }
+
+ for (NetworkStats.Entry e : stats) {
+ assertEquals(e, entries.remove(0));
+ }
+ }
+
private static void assertContains(NetworkStats stats, String iface, int uid, int set,
int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) {
@@ -1057,22 +1083,22 @@
private static void assertValues(
NetworkStats.Entry entry, String iface, int uid, int set, int tag, int metered,
int roaming, int defaultNetwork) {
- assertEquals(iface, entry.iface);
- assertEquals(uid, entry.uid);
- assertEquals(set, entry.set);
- assertEquals(tag, entry.tag);
- assertEquals(metered, entry.metered);
- assertEquals(roaming, entry.roaming);
- assertEquals(defaultNetwork, entry.defaultNetwork);
+ assertEquals(iface, entry.getIface());
+ assertEquals(uid, entry.getUid());
+ assertEquals(set, entry.getSet());
+ assertEquals(tag, entry.getTag());
+ assertEquals(metered, entry.getMetered());
+ assertEquals(roaming, entry.getRoaming());
+ assertEquals(defaultNetwork, entry.getDefaultNetwork());
}
private static void assertValues(NetworkStats.Entry entry, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) {
- assertEquals(rxBytes, entry.rxBytes);
- assertEquals(rxPackets, entry.rxPackets);
- assertEquals(txBytes, entry.txBytes);
- assertEquals(txPackets, entry.txPackets);
- assertEquals(operations, entry.operations);
+ assertEquals(rxBytes, entry.getRxBytes());
+ assertEquals(rxPackets, entry.getRxPackets());
+ assertEquals(txBytes, entry.getTxBytes());
+ assertEquals(txPackets, entry.getTxPackets());
+ assertEquals(operations, entry.getOperations());
}
}
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index 49c7271..0c3bee3 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -22,13 +22,18 @@
import android.net.NetworkIdentity.OEM_NONE
import android.net.NetworkIdentity.OEM_PAID
import android.net.NetworkIdentity.OEM_PRIVATE
-import android.net.NetworkIdentity.SUBTYPE_COMBINED
import android.net.NetworkIdentity.buildNetworkIdentity
import android.net.NetworkStats.DEFAULT_NETWORK_ALL
import android.net.NetworkStats.METERED_ALL
+import android.net.NetworkStats.METERED_NO
+import android.net.NetworkStats.METERED_YES
import android.net.NetworkStats.ROAMING_ALL
+import android.net.NetworkTemplate.MATCH_BLUETOOTH
+import android.net.NetworkTemplate.MATCH_CARRIER
+import android.net.NetworkTemplate.MATCH_ETHERNET
import android.net.NetworkTemplate.MATCH_MOBILE
import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD
+import android.net.NetworkTemplate.MATCH_PROXY
import android.net.NetworkTemplate.MATCH_WIFI
import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD
import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA
@@ -36,58 +41,70 @@
import android.net.NetworkTemplate.OEM_MANAGED_ALL
import android.net.NetworkTemplate.OEM_MANAGED_NO
import android.net.NetworkTemplate.OEM_MANAGED_YES
-import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT
-import android.net.NetworkTemplate.WIFI_NETWORKID_ALL
+import android.net.NetworkTemplate.WIFI_NETWORK_KEY_ALL
import android.net.NetworkTemplate.buildTemplateCarrierMetered
+import android.net.NetworkTemplate.buildTemplateMobileAll
+import android.net.NetworkTemplate.buildTemplateMobileWildcard
import android.net.NetworkTemplate.buildTemplateMobileWithRatType
import android.net.NetworkTemplate.buildTemplateWifi
import android.net.NetworkTemplate.buildTemplateWifiWildcard
+import android.net.NetworkTemplate.normalize
+import android.net.wifi.WifiInfo
import android.os.Build
import android.telephony.TelephonyManager
+import com.android.net.module.util.NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
+import com.android.net.module.util.NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.SC_V2
import com.android.testutils.assertParcelSane
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
private const val TEST_IMSI1 = "imsi1"
private const val TEST_IMSI2 = "imsi2"
-private const val TEST_SSID1 = "ssid1"
-private const val TEST_SSID2 = "ssid2"
+private const val TEST_IMSI3 = "imsi3"
+private const val TEST_WIFI_KEY1 = "wifiKey1"
+private const val TEST_WIFI_KEY2 = "wifiKey2"
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class NetworkTemplateTest {
private val mockContext = mock(Context::class.java)
+ private val mockWifiInfo = mock(WifiInfo::class.java)
private fun buildMobileNetworkState(subscriberId: String): NetworkStateSnapshot =
buildNetworkState(TYPE_MOBILE, subscriberId = subscriberId)
- private fun buildWifiNetworkState(subscriberId: String?, ssid: String?): NetworkStateSnapshot =
- buildNetworkState(TYPE_WIFI, subscriberId = subscriberId, ssid = ssid)
+ private fun buildWifiNetworkState(subscriberId: String?, wifiKey: String?):
+ NetworkStateSnapshot = buildNetworkState(TYPE_WIFI,
+ subscriberId = subscriberId, wifiKey = wifiKey)
private fun buildNetworkState(
type: Int,
subscriberId: String? = null,
- ssid: String? = null,
+ wifiKey: String? = null,
oemManaged: Int = OEM_NONE,
metered: Boolean = true
): NetworkStateSnapshot {
+ `when`(mockWifiInfo.getCurrentNetworkKey()).thenReturn(wifiKey)
val lp = LinkProperties()
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !metered)
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
- setSSID(ssid)
setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID,
(oemManaged and OEM_PAID) == OEM_PAID)
setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE,
(oemManaged and OEM_PRIVATE) == OEM_PRIVATE)
+ setTransportInfo(mockWifiInfo)
}
return NetworkStateSnapshot(mock(Network::class.java), caps, lp, subscriberId, type)
}
@@ -110,64 +127,116 @@
val identMobileImsi1 = buildNetworkIdentity(mockContext,
buildMobileNetworkState(TEST_IMSI1),
false, TelephonyManager.NETWORK_TYPE_UMTS)
- val identWifiImsiNullSsid1 = buildNetworkIdentity(
- mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0)
- val identWifiImsi1Ssid1 = buildNetworkIdentity(
- mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0)
+ val identWifiImsiNullKey1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(null, TEST_WIFI_KEY1), true, 0)
+ val identWifiImsi1Key1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_WIFI_KEY1), true, 0)
templateWifiWildcard.assertDoesNotMatch(identMobileImsi1)
- templateWifiWildcard.assertMatches(identWifiImsiNullSsid1)
- templateWifiWildcard.assertMatches(identWifiImsi1Ssid1)
+ templateWifiWildcard.assertMatches(identWifiImsiNullKey1)
+ templateWifiWildcard.assertMatches(identWifiImsi1Key1)
}
@Test
fun testWifiMatches() {
- val templateWifiSsid1 = buildTemplateWifi(TEST_SSID1)
- val templateWifiSsid1ImsiNull = buildTemplateWifi(TEST_SSID1, null)
- val templateWifiSsid1Imsi1 = buildTemplateWifi(TEST_SSID1, TEST_IMSI1)
- val templateWifiSsidAllImsi1 = buildTemplateWifi(WIFI_NETWORKID_ALL, TEST_IMSI1)
+ val templateWifiKey1 = buildTemplateWifi(TEST_WIFI_KEY1)
+ val templateWifiKey1ImsiNull = buildTemplateWifi(TEST_WIFI_KEY1, null)
+ val templateWifiKey1Imsi1 = buildTemplateWifi(TEST_WIFI_KEY1, TEST_IMSI1)
+ val templateWifiKeyAllImsi1 = buildTemplateWifi(WIFI_NETWORK_KEY_ALL, TEST_IMSI1)
val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1),
false, TelephonyManager.NETWORK_TYPE_UMTS)
- val identWifiImsiNullSsid1 = buildNetworkIdentity(
- mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0)
- val identWifiImsi1Ssid1 = buildNetworkIdentity(
- mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0)
- val identWifiImsi2Ssid1 = buildNetworkIdentity(
- mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0)
- val identWifiImsi1Ssid2 = buildNetworkIdentity(
- mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID2), true, 0)
+ val identWifiImsiNullKey1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(null, TEST_WIFI_KEY1), true, 0)
+ val identWifiImsi1Key1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_WIFI_KEY1), true, 0)
+ val identWifiImsi2Key1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_WIFI_KEY1), true, 0)
+ val identWifiImsi1Key2 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_WIFI_KEY2), true, 0)
- // Verify that template with SSID only matches any subscriberId and specific SSID.
- templateWifiSsid1.assertDoesNotMatch(identMobile1)
- templateWifiSsid1.assertMatches(identWifiImsiNullSsid1)
- templateWifiSsid1.assertMatches(identWifiImsi1Ssid1)
- templateWifiSsid1.assertMatches(identWifiImsi2Ssid1)
- templateWifiSsid1.assertDoesNotMatch(identWifiImsi1Ssid2)
+ // Verify that template with WiFi Network Key only matches any subscriberId and
+ // specific WiFi Network Key.
+ templateWifiKey1.assertDoesNotMatch(identMobile1)
+ templateWifiKey1.assertMatches(identWifiImsiNullKey1)
+ templateWifiKey1.assertMatches(identWifiImsi1Key1)
+ templateWifiKey1.assertMatches(identWifiImsi2Key1)
+ templateWifiKey1.assertDoesNotMatch(identWifiImsi1Key2)
- // Verify that template with SSID1 and null imsi matches any network with
- // SSID1 and null imsi.
- templateWifiSsid1ImsiNull.assertDoesNotMatch(identMobile1)
- templateWifiSsid1ImsiNull.assertMatches(identWifiImsiNullSsid1)
- templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi1Ssid1)
- templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi2Ssid1)
- templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi1Ssid2)
+ // Verify that template with WiFi Network Key1 and null imsi matches any network with
+ // WiFi Network Key1 and null imsi.
+ templateWifiKey1ImsiNull.assertDoesNotMatch(identMobile1)
+ templateWifiKey1ImsiNull.assertMatches(identWifiImsiNullKey1)
+ templateWifiKey1ImsiNull.assertDoesNotMatch(identWifiImsi1Key1)
+ templateWifiKey1ImsiNull.assertDoesNotMatch(identWifiImsi2Key1)
+ templateWifiKey1ImsiNull.assertDoesNotMatch(identWifiImsi1Key2)
- // Verify that template with SSID1 and imsi1 matches any network with
- // SSID1 and imsi1.
- templateWifiSsid1Imsi1.assertDoesNotMatch(identMobile1)
- templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsiNullSsid1)
- templateWifiSsid1Imsi1.assertMatches(identWifiImsi1Ssid1)
- templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsi2Ssid1)
- templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsi1Ssid2)
+ // Verify that template with WiFi Network Key1 and imsi1 matches any network with
+ // WiFi Network Key1 and imsi1.
+ templateWifiKey1Imsi1.assertDoesNotMatch(identMobile1)
+ templateWifiKey1Imsi1.assertDoesNotMatch(identWifiImsiNullKey1)
+ templateWifiKey1Imsi1.assertMatches(identWifiImsi1Key1)
+ templateWifiKey1Imsi1.assertDoesNotMatch(identWifiImsi2Key1)
+ templateWifiKey1Imsi1.assertDoesNotMatch(identWifiImsi1Key2)
- // Verify that template with SSID all and imsi1 matches any network with
- // any SSID and imsi1.
- templateWifiSsidAllImsi1.assertDoesNotMatch(identMobile1)
- templateWifiSsidAllImsi1.assertDoesNotMatch(identWifiImsiNullSsid1)
- templateWifiSsidAllImsi1.assertMatches(identWifiImsi1Ssid1)
- templateWifiSsidAllImsi1.assertDoesNotMatch(identWifiImsi2Ssid1)
- templateWifiSsidAllImsi1.assertMatches(identWifiImsi1Ssid2)
+ // Verify that template with WiFi Network Key all and imsi1 matches any network with
+ // any WiFi Network Key and imsi1.
+ templateWifiKeyAllImsi1.assertDoesNotMatch(identMobile1)
+ templateWifiKeyAllImsi1.assertDoesNotMatch(identWifiImsiNullKey1)
+ templateWifiKeyAllImsi1.assertMatches(identWifiImsi1Key1)
+ templateWifiKeyAllImsi1.assertDoesNotMatch(identWifiImsi2Key1)
+ templateWifiKeyAllImsi1.assertMatches(identWifiImsi1Key2)
+ }
+
+ @Test
+ fun testMobileMatches() {
+ val templateMobileImsi1 = buildTemplateMobileAll(TEST_IMSI1)
+ val templateMobileImsi2WithRatType = buildTemplateMobileWithRatType(TEST_IMSI2,
+ TelephonyManager.NETWORK_TYPE_UMTS, METERED_YES)
+
+ val mobileImsi1 = buildNetworkState(TYPE_MOBILE, TEST_IMSI1, null /* wifiKey */,
+ OEM_NONE, true /* metered */)
+ val identMobile1 = buildNetworkIdentity(mockContext, mobileImsi1,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ val mobileImsi2 = buildMobileNetworkState(TEST_IMSI2)
+ val identMobile2Umts = buildNetworkIdentity(mockContext, mobileImsi2,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ val identWifiImsi1Key1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_WIFI_KEY1), true, 0)
+
+ // Verify that the template matches type and the subscriberId.
+ templateMobileImsi1.assertMatches(identMobile1)
+ templateMobileImsi2WithRatType.assertMatches(identMobile2Umts)
+
+ // Verify that the template does not match the different subscriberId.
+ templateMobileImsi1.assertDoesNotMatch(identMobile2Umts)
+ templateMobileImsi2WithRatType.assertDoesNotMatch(identMobile1)
+
+ // Verify that the different type does not match.
+ templateMobileImsi1.assertDoesNotMatch(identWifiImsi1Key1)
+ }
+
+ @Test
+ fun testMobileWildcardMatches() {
+ val templateMobileWildcard = buildTemplateMobileWildcard()
+ val templateMobileNullImsiWithRatType = buildTemplateMobileWithRatType(null,
+ TelephonyManager.NETWORK_TYPE_UMTS, METERED_ALL)
+
+ val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1)
+ val identMobile1 = buildNetworkIdentity(mockContext, mobileImsi1,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ // Verify that the template matches any subscriberId.
+ templateMobileWildcard.assertMatches(identMobile1)
+ templateMobileNullImsiWithRatType.assertMatches(identMobile1)
+
+ val identWifiImsi1Key1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_WIFI_KEY1), true, 0)
+
+ // Verify that the different type does not match.
+ templateMobileWildcard.assertDoesNotMatch(identWifiImsi1Key1)
+ templateMobileNullImsiWithRatType.assertDoesNotMatch(identWifiImsi1Key1)
}
@Test
@@ -175,13 +244,14 @@
val templateCarrierImsi1Metered = buildTemplateCarrierMetered(TEST_IMSI1)
val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1)
- val mobileImsi1Unmetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI1, null /* ssid */,
- OEM_NONE, false /* metered */)
+ val mobileImsi1Unmetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI1,
+ null /* wifiKey */, OEM_NONE, false /* metered */)
val mobileImsi2 = buildMobileNetworkState(TEST_IMSI2)
- val wifiSsid1 = buildWifiNetworkState(null /* subscriberId */, TEST_SSID1)
- val wifiImsi1Ssid1 = buildWifiNetworkState(TEST_IMSI1, TEST_SSID1)
- val wifiImsi1Ssid1Unmetered = buildNetworkState(TYPE_WIFI, TEST_IMSI1, TEST_SSID1,
- OEM_NONE, false /* metered */)
+ val wifiKey1 = buildWifiNetworkState(null /* subscriberId */,
+ TEST_WIFI_KEY1)
+ val wifiImsi1Key1 = buildWifiNetworkState(TEST_IMSI1, TEST_WIFI_KEY1)
+ val wifiImsi1Key1Unmetered = buildNetworkState(TYPE_WIFI, TEST_IMSI1,
+ TEST_WIFI_KEY1, OEM_NONE, false /* metered */)
val identMobileImsi1Metered = buildNetworkIdentity(mockContext,
mobileImsi1, false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
@@ -190,86 +260,160 @@
TelephonyManager.NETWORK_TYPE_UMTS)
val identMobileImsi2Metered = buildNetworkIdentity(mockContext,
mobileImsi2, false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
- val identWifiSsid1Metered = buildNetworkIdentity(
- mockContext, wifiSsid1, true /* defaultNetwork */, 0 /* subType */)
+ val identWifiKey1Metered = buildNetworkIdentity(
+ mockContext, wifiKey1, true /* defaultNetwork */, 0 /* subType */)
val identCarrierWifiImsi1Metered = buildNetworkIdentity(
- mockContext, wifiImsi1Ssid1, true /* defaultNetwork */, 0 /* subType */)
+ mockContext, wifiImsi1Key1, true /* defaultNetwork */, 0 /* subType */)
val identCarrierWifiImsi1NonMetered = buildNetworkIdentity(mockContext,
- wifiImsi1Ssid1Unmetered, true /* defaultNetwork */, 0 /* subType */)
+ wifiImsi1Key1Unmetered, true /* defaultNetwork */, 0 /* subType */)
templateCarrierImsi1Metered.assertMatches(identMobileImsi1Metered)
templateCarrierImsi1Metered.assertDoesNotMatch(identMobileImsi1Unmetered)
templateCarrierImsi1Metered.assertDoesNotMatch(identMobileImsi2Metered)
- templateCarrierImsi1Metered.assertDoesNotMatch(identWifiSsid1Metered)
+ templateCarrierImsi1Metered.assertDoesNotMatch(identWifiKey1Metered)
templateCarrierImsi1Metered.assertMatches(identCarrierWifiImsi1Metered)
templateCarrierImsi1Metered.assertDoesNotMatch(identCarrierWifiImsi1NonMetered)
}
+ // TODO: Refactor this test to reduce the line of codes.
@Test
fun testRatTypeGroupMatches() {
- val stateMobile = buildMobileNetworkState(TEST_IMSI1)
+ val stateMobileImsi1Metered = buildMobileNetworkState(TEST_IMSI1)
+ val stateMobileImsi1NonMetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI1,
+ null /* wifiKey */, OEM_NONE, false /* metered */)
+ val stateMobileImsi2NonMetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI2,
+ null /* wifiKey */, OEM_NONE, false /* metered */)
+
// Build UMTS template that matches mobile identities with RAT in the same
// group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}.
- val templateUmts = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS)
+ val templateUmtsMetered = buildTemplateMobileWithRatType(null,
+ TelephonyManager.NETWORK_TYPE_UMTS, METERED_YES)
// Build normal template that matches mobile identities with any RAT and IMSI.
- val templateAll = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL)
+ val templateAllMetered = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL,
+ METERED_YES)
// Build template with UNKNOWN RAT that matches mobile identities with RAT that
// cannot be determined.
- val templateUnknown =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN)
+ val templateUnknownMetered =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ METERED_YES)
- val identUmts = buildNetworkIdentity(
- mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_UMTS)
- val identHsdpa = buildNetworkIdentity(
- mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_HSDPA)
- val identLte = buildNetworkIdentity(
- mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_LTE)
- val identCombined = buildNetworkIdentity(
- mockContext, stateMobile, false, SUBTYPE_COMBINED)
- val identImsi2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2),
- false, TelephonyManager.NETWORK_TYPE_UMTS)
+ val templateUmtsNonMetered = buildTemplateMobileWithRatType(null,
+ TelephonyManager.NETWORK_TYPE_UMTS, METERED_NO)
+ val templateAllNonMetered = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL,
+ METERED_NO)
+ val templateUnknownNonMetered =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ METERED_NO)
+
+ val identUmtsMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1Metered, false, TelephonyManager.NETWORK_TYPE_UMTS)
+ val identHsdpaMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1Metered, false, TelephonyManager.NETWORK_TYPE_HSDPA)
+ val identLteMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1Metered, false, TelephonyManager.NETWORK_TYPE_LTE)
+ val identCombinedMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1Metered, false, NetworkTemplate.NETWORK_TYPE_ALL)
+ val identImsi2UmtsMetered = buildNetworkIdentity(mockContext,
+ buildMobileNetworkState(TEST_IMSI2), false, TelephonyManager.NETWORK_TYPE_UMTS)
val identWifi = buildNetworkIdentity(
- mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0)
+ mockContext, buildWifiNetworkState(null, TEST_WIFI_KEY1), true, 0)
- // Assert that identity with the same RAT matches.
- templateUmts.assertMatches(identUmts)
- templateAll.assertMatches(identUmts)
- templateUnknown.assertDoesNotMatch(identUmts)
+ val identUmtsNonMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1NonMetered, false, TelephonyManager.NETWORK_TYPE_UMTS)
+ val identHsdpaNonMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1NonMetered, false,
+ TelephonyManager.NETWORK_TYPE_HSDPA)
+ val identLteNonMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1NonMetered, false, TelephonyManager.NETWORK_TYPE_LTE)
+ val identCombinedNonMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1NonMetered, false, NetworkTemplate.NETWORK_TYPE_ALL)
+ val identImsi2UmtsNonMetered = buildNetworkIdentity(mockContext,
+ stateMobileImsi2NonMetered, false, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ // Assert that identity with the same RAT and meteredness matches.
+ // Verify metered template.
+ templateUmtsMetered.assertMatches(identUmtsMetered)
+ templateAllMetered.assertMatches(identUmtsMetered)
+ templateUnknownMetered.assertDoesNotMatch(identUmtsMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertMatches(identUmtsNonMetered)
+ templateAllNonMetered.assertMatches(identUmtsNonMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identUmtsNonMetered)
+
+ // Assert that identity with the same RAT but meteredness is different.
+ // Thus, it does not match.
+ templateUmtsNonMetered.assertDoesNotMatch(identUmtsMetered)
+ templateAllNonMetered.assertDoesNotMatch(identUmtsMetered)
+
// Assert that identity with the RAT within the same group matches.
- templateUmts.assertMatches(identHsdpa)
- templateAll.assertMatches(identHsdpa)
- templateUnknown.assertDoesNotMatch(identHsdpa)
+ // Verify metered template.
+ templateUmtsMetered.assertMatches(identHsdpaMetered)
+ templateAllMetered.assertMatches(identHsdpaMetered)
+ templateUnknownMetered.assertDoesNotMatch(identHsdpaMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertMatches(identHsdpaNonMetered)
+ templateAllNonMetered.assertMatches(identHsdpaNonMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identHsdpaNonMetered)
+
// Assert that identity with the RAT out of the same group only matches template with
// NETWORK_TYPE_ALL.
- templateUmts.assertDoesNotMatch(identLte)
- templateAll.assertMatches(identLte)
- templateUnknown.assertDoesNotMatch(identLte)
+ // Verify metered template.
+ templateUmtsMetered.assertDoesNotMatch(identLteMetered)
+ templateAllMetered.assertMatches(identLteMetered)
+ templateUnknownMetered.assertDoesNotMatch(identLteMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertDoesNotMatch(identLteNonMetered)
+ templateAllNonMetered.assertMatches(identLteNonMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identLteNonMetered)
+ // Verify non-metered template does not match identity with metered.
+ templateAllNonMetered.assertDoesNotMatch(identLteMetered)
+
// Assert that identity with combined RAT only matches with template with NETWORK_TYPE_ALL
// and NETWORK_TYPE_UNKNOWN.
- templateUmts.assertDoesNotMatch(identCombined)
- templateAll.assertMatches(identCombined)
- templateUnknown.assertMatches(identCombined)
+ // Verify metered template.
+ templateUmtsMetered.assertDoesNotMatch(identCombinedMetered)
+ templateAllMetered.assertMatches(identCombinedMetered)
+ templateUnknownMetered.assertMatches(identCombinedMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertDoesNotMatch(identCombinedNonMetered)
+ templateAllNonMetered.assertMatches(identCombinedNonMetered)
+ templateUnknownNonMetered.assertMatches(identCombinedNonMetered)
+ // Verify that identity with metered does not match non-metered template.
+ templateAllNonMetered.assertDoesNotMatch(identCombinedMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identCombinedMetered)
+
// Assert that identity with different IMSI matches.
- templateUmts.assertMatches(identImsi2)
- templateAll.assertMatches(identImsi2)
- templateUnknown.assertDoesNotMatch(identImsi2)
+ // Verify metered template.
+ templateUmtsMetered.assertMatches(identImsi2UmtsMetered)
+ templateAllMetered.assertMatches(identImsi2UmtsMetered)
+ templateUnknownMetered.assertDoesNotMatch(identImsi2UmtsMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertMatches(identImsi2UmtsNonMetered)
+ templateAllNonMetered.assertMatches(identImsi2UmtsNonMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identImsi2UmtsNonMetered)
+ // Verify that the same RAT but different meteredness should not match.
+ templateUmtsNonMetered.assertDoesNotMatch(identImsi2UmtsMetered)
+ templateAllNonMetered.assertDoesNotMatch(identImsi2UmtsMetered)
+
// Assert that wifi identity does not match.
- templateUmts.assertDoesNotMatch(identWifi)
- templateAll.assertDoesNotMatch(identWifi)
- templateUnknown.assertDoesNotMatch(identWifi)
+ templateUmtsMetered.assertDoesNotMatch(identWifi)
+ templateUnknownMetered.assertDoesNotMatch(identWifi)
+ templateUmtsNonMetered.assertDoesNotMatch(identWifi)
+ templateUnknownNonMetered.assertDoesNotMatch(identWifi)
}
@Test
fun testParcelUnparcel() {
- val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL,
- ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE,
+ val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null,
+ arrayOf<String>(), METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
+ TelephonyManager.NETWORK_TYPE_LTE, OEM_MANAGED_ALL,
+ SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ val templateWifi = NetworkTemplate(MATCH_WIFI, null, null,
+ arrayOf(TEST_WIFI_KEY1), METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, 0,
OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
- val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL,
- ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_ALL,
- SUBSCRIBER_ID_MATCH_RULE_EXACT)
- val templateOem = NetworkTemplate(MATCH_MOBILE, null, null, null, METERED_ALL,
- ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_YES,
- SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ val templateOem = NetworkTemplate(MATCH_MOBILE, null, null,
+ arrayOf<String>(), METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, 0,
+ OEM_MANAGED_YES, SUBSCRIBER_ID_MATCH_RULE_EXACT)
assertParcelSane(templateMobile, 10)
assertParcelSane(templateWifi, 10)
assertParcelSane(templateOem, 10)
@@ -307,39 +451,43 @@
* @param subscriberId To be populated with {@code TEST_IMSI*} only if networkType is
* {@code TYPE_MOBILE}. May be left as null when matchType is
* {@link NetworkTemplate.MATCH_MOBILE_WILDCARD}.
- * @param templateSsid Top be populated with {@code TEST_SSID*} only if networkType is
+ * @param templateWifiKey Top be populated with {@code TEST_WIFI_KEY*} only if networkType is
* {@code TYPE_WIFI}. May be left as null when matchType is
* {@link NetworkTemplate.MATCH_WIFI_WILDCARD}.
- * @param identSsid If networkType is {@code TYPE_WIFI}, this value must *NOT* be null. Provide
- * one of {@code TEST_SSID*}.
+ * @param identWifiKey If networkType is {@code TYPE_WIFI}, this value must *NOT* be null. Provide
+ * one of {@code TEST_WIFI_KEY*}.
*/
private fun matchOemManagedIdent(
networkType: Int,
matchType: Int,
subscriberId: String? = null,
- templateSsid: String? = null,
- identSsid: String? = null
+ templateWifiKey: String? = null,
+ identWifiKey: String? = null
) {
val oemManagedStates = arrayOf(OEM_NONE, OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE)
val matchSubscriberIds = arrayOf(subscriberId)
+ val matchWifiNetworkKeys = arrayOf(templateWifiKey)
val templateOemYes = NetworkTemplate(matchType, subscriberId, matchSubscriberIds,
- templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_YES, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ matchWifiNetworkKeys, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_YES,
+ SUBSCRIBER_ID_MATCH_RULE_EXACT)
val templateOemAll = NetworkTemplate(matchType, subscriberId, matchSubscriberIds,
- templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ matchWifiNetworkKeys, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
+ SUBSCRIBER_ID_MATCH_RULE_EXACT)
for (identityOemManagedState in oemManagedStates) {
val ident = buildNetworkIdentity(mockContext, buildNetworkState(networkType,
- subscriberId, identSsid, identityOemManagedState), /*defaultNetwork=*/false,
- /*subType=*/0)
+ subscriberId, identWifiKey, identityOemManagedState),
+ /*defaultNetwork=*/false, /*subType=*/0)
// Create a template with each OEM managed type and match it against the NetworkIdentity
for (templateOemManagedState in oemManagedStates) {
val template = NetworkTemplate(matchType, subscriberId, matchSubscriberIds,
- templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
- NETWORK_TYPE_ALL, templateOemManagedState, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ matchWifiNetworkKeys, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, templateOemManagedState,
+ SUBSCRIBER_ID_MATCH_RULE_EXACT)
if (identityOemManagedState == templateOemManagedState) {
template.assertMatches(ident)
} else {
@@ -361,8 +509,186 @@
fun testOemManagedMatchesIdent() {
matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE, subscriberId = TEST_IMSI1)
matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE_WILDCARD)
- matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI, templateSsid = TEST_SSID1,
- identSsid = TEST_SSID1)
- matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI_WILDCARD, identSsid = TEST_SSID1)
+ matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI, templateWifiKey = TEST_WIFI_KEY1,
+ identWifiKey = TEST_WIFI_KEY1)
+ matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI_WILDCARD,
+ identWifiKey = TEST_WIFI_KEY1)
+ }
+
+ @Test
+ fun testNormalize() {
+ var mergedImsiList = listOf(arrayOf(TEST_IMSI1, TEST_IMSI2))
+ val identMobileImsi1 = buildNetworkIdentity(mockContext,
+ buildMobileNetworkState(TEST_IMSI1), false /* defaultNetwork */,
+ TelephonyManager.NETWORK_TYPE_UMTS)
+ val identMobileImsi2 = buildNetworkIdentity(mockContext,
+ buildMobileNetworkState(TEST_IMSI2), false /* defaultNetwork */,
+ TelephonyManager.NETWORK_TYPE_UMTS)
+ val identMobileImsi3 = buildNetworkIdentity(mockContext,
+ buildMobileNetworkState(TEST_IMSI3), false /* defaultNetwork */,
+ TelephonyManager.NETWORK_TYPE_UMTS)
+ val identWifiImsi1Key1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_WIFI_KEY1), true, 0)
+ val identWifiImsi2Key1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_WIFI_KEY1), true, 0)
+ val identWifiImsi3WifiKey1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI3, TEST_WIFI_KEY1), true, 0)
+
+ normalize(buildTemplateMobileAll(TEST_IMSI1), mergedImsiList).also {
+ it.assertMatches(identMobileImsi1)
+ it.assertMatches(identMobileImsi2)
+ it.assertDoesNotMatch(identMobileImsi3)
+ }
+ normalize(buildTemplateCarrierMetered(TEST_IMSI1), mergedImsiList).also {
+ it.assertMatches(identMobileImsi1)
+ it.assertMatches(identMobileImsi2)
+ it.assertDoesNotMatch(identMobileImsi3)
+ }
+ normalize(buildTemplateWifi(TEST_WIFI_KEY1, TEST_IMSI1), mergedImsiList).also {
+ it.assertMatches(identWifiImsi1Key1)
+ it.assertMatches(identWifiImsi2Key1)
+ it.assertDoesNotMatch(identWifiImsi3WifiKey1)
+ }
+ normalize(buildTemplateMobileWildcard(), mergedImsiList).also {
+ it.assertMatches(identMobileImsi1)
+ it.assertMatches(identMobileImsi2)
+ it.assertMatches(identMobileImsi3)
+ }
+ }
+
+ @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+ @Test
+ fun testBuilderMatchRules() {
+ // Verify unknown match rules cannot construct templates.
+ listOf(Integer.MIN_VALUE, -1, Integer.MAX_VALUE).forEach {
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(it).build()
+ }
+ }
+
+ // Verify hidden match rules cannot construct templates.
+ listOf(MATCH_WIFI_WILDCARD, MATCH_MOBILE_WILDCARD, MATCH_PROXY).forEach {
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(it).build()
+ }
+ }
+
+ // Verify template which matches metered cellular and carrier networks with
+ // the given IMSI. See buildTemplateMobileAll and buildTemplateCarrierMetered.
+ listOf(MATCH_MOBILE, MATCH_CARRIER).forEach { matchRule ->
+ NetworkTemplate.Builder(matchRule).setSubscriberIds(setOf(TEST_IMSI1))
+ .setMeteredness(METERED_YES).build().let {
+ val expectedTemplate = NetworkTemplate(matchRule, TEST_IMSI1,
+ arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ assertEquals(expectedTemplate, it)
+ }
+ }
+
+ // Verify carrier template cannot be created without IMSI.
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(MATCH_CARRIER).build()
+ }
+
+ // Verify template which matches metered cellular networks,
+ // regardless of IMSI. See buildTemplateMobileWildcard.
+ NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_MOBILE_WILDCARD, null /*subscriberId*/,
+ null /*subscriberIds*/, arrayOf<String>(),
+ METERED_YES, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches metered cellular networks and ratType.
+ // See NetworkTemplate#buildTemplateMobileWithRatType.
+ NetworkTemplate.Builder(MATCH_MOBILE).setSubscriberIds(setOf(TEST_IMSI1))
+ .setMeteredness(METERED_YES).setRatType(TelephonyManager.NETWORK_TYPE_UMTS)
+ .build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1,
+ arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_UMTS,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches all wifi networks,
+ // regardless of Wifi Network Key. See buildTemplateWifiWildcard and buildTemplateWifi.
+ NetworkTemplate.Builder(MATCH_WIFI).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_WIFI_WILDCARD, null /*subscriberId*/,
+ null /*subscriberIds*/, arrayOf<String>(),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches wifi networks with the given Wifi Network Key.
+ // See buildTemplateWifi(wifiNetworkKey).
+ NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_WIFI, null /*subscriberId*/,
+ null /*subscriberIds*/, arrayOf(TEST_WIFI_KEY1),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches all wifi networks with the
+ // given Wifi Network Key, and IMSI. See buildTemplateWifi(wifiNetworkKey, subscriberId).
+ NetworkTemplate.Builder(MATCH_WIFI).setSubscriberIds(setOf(TEST_IMSI1))
+ .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_WIFI, TEST_IMSI1,
+ arrayOf(TEST_IMSI1), arrayOf(TEST_WIFI_KEY1),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches ethernet and bluetooth networks.
+ // See buildTemplateEthernet and buildTemplateBluetooth.
+ listOf(MATCH_ETHERNET, MATCH_BLUETOOTH).forEach { matchRule ->
+ NetworkTemplate.Builder(matchRule).build().let {
+ val expectedTemplate = NetworkTemplate(matchRule, null /*subscriberId*/,
+ null /*subscriberIds*/, arrayOf<String>(),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+ }
+ }
+
+ @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+ @Test
+ fun testBuilderWifiNetworkKeys() {
+ // Verify template builder which generates same template with the given different
+ // sequence keys.
+ NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(
+ setOf(TEST_WIFI_KEY1, TEST_WIFI_KEY2)).build().let {
+ val expectedTemplate = NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(
+ setOf(TEST_WIFI_KEY2, TEST_WIFI_KEY1)).build()
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches non-wifi networks with the given key is invalid.
+ listOf(MATCH_MOBILE, MATCH_CARRIER, MATCH_ETHERNET, MATCH_BLUETOOTH, -1,
+ Integer.MAX_VALUE).forEach { matchRule ->
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(matchRule).setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build()
+ }
+ }
+
+ // Verify template which matches wifi networks with the given null key is invalid.
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf(null)).build()
+ }
+
+ // Verify template which matches wifi wildcard with the given empty key set.
+ NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf<String>()).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_WIFI_WILDCARD, null /*subscriberId*/,
+ arrayOf<String>() /*subscriberIds*/, arrayOf<String>(),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
}
}
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index 31c8927..30b8fcd 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -16,36 +16,36 @@
package android.net.nsd;
+import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
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 android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import androidx.test.filters.SmallTest;
-import com.android.internal.util.AsyncChannel;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
-import com.android.testutils.HandlerUtils;
+import com.android.testutils.ExceptionUtils;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -56,11 +56,15 @@
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
@Mock Context mContext;
@Mock INsdManager mService;
- MockServiceHandler mServiceHandler;
+ @Mock INsdServiceConnector mServiceConn;
NsdManager mManager;
+ INsdManagerCallback mCallback;
long mTimeoutMs = 200; // non-final so that tests can adjust the value.
@@ -68,24 +72,85 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mServiceHandler = spy(MockServiceHandler.create(mContext));
- doReturn(new Messenger(mServiceHandler)).when(mService).getMessenger();
-
- mManager = makeManager();
- }
-
- @After
- public void tearDown() throws Exception {
- HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs);
- mServiceHandler.chan.disconnect();
- mServiceHandler.stop();
- if (mManager != null) {
- mManager.disconnect();
- }
+ doReturn(mServiceConn).when(mService).connect(any());
+ mManager = new NsdManager(mContext, mService);
+ final ArgumentCaptor<INsdManagerCallback> cbCaptor = ArgumentCaptor.forClass(
+ INsdManagerCallback.class);
+ verify(mService).connect(cbCaptor.capture());
+ mCallback = cbCaptor.getValue();
}
@Test
- public void testResolveService() {
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testResolveServiceS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
+ doTestResolveService();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testResolveServicePreS() throws Exception {
+ verify(mServiceConn).startDaemon();
+ doTestResolveService();
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testDiscoverServiceS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
+ doTestDiscoverService();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testDiscoverServicePreS() throws Exception {
+ verify(mServiceConn).startDaemon();
+ doTestDiscoverService();
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testParallelResolveServiceS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
+ doTestParallelResolveService();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testParallelResolveServicePreS() throws Exception {
+ verify(mServiceConn).startDaemon();
+ doTestParallelResolveService();
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testInvalidCallsS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
+ doTestInvalidCalls();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testInvalidCallsPreS() throws Exception {
+ verify(mServiceConn).startDaemon();
+ doTestInvalidCalls();
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testRegisterServiceS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
+ doTestRegisterService();
+ }
+
+ @Test
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testRegisterServicePreS() throws Exception {
+ verify(mServiceConn).startDaemon();
+ doTestRegisterService();
+ }
+
+ private void doTestResolveService() throws Exception {
NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -93,19 +158,19 @@
NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class);
manager.resolveService(request, listener);
- int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+ int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
int err = 33;
- sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null);
+ mCallback.onResolveServiceFailed(key1, err);
verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err);
manager.resolveService(request, listener);
- int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE);
- sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply);
+ int key2 = getRequestKey(req ->
+ verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
+ mCallback.onResolveServiceSucceeded(key2, reply);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
}
- @Test
- public void testParallelResolveService() {
+ private void doTestParallelResolveService() throws Exception {
NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -115,20 +180,20 @@
NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class);
manager.resolveService(request, listener1);
- int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+ int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
manager.resolveService(request, listener2);
- int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+ int key2 = getRequestKey(req ->
+ verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
- sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply);
- sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply);
+ mCallback.onResolveServiceSucceeded(key2, reply);
+ mCallback.onResolveServiceSucceeded(key1, reply);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
}
- @Test
- public void testRegisterService() {
+ private void doTestRegisterService() throws Exception {
NsdManager manager = mManager;
NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type");
@@ -140,40 +205,43 @@
// Register two services
manager.registerService(request1, PROTOCOL, listener1);
- int key1 = verifyRequest(NsdManager.REGISTER_SERVICE);
+ int key1 = getRequestKey(req -> verify(mServiceConn).registerService(req.capture(), any()));
manager.registerService(request2, PROTOCOL, listener2);
- int key2 = verifyRequest(NsdManager.REGISTER_SERVICE);
+ int key2 = getRequestKey(req ->
+ verify(mServiceConn, times(2)).registerService(req.capture(), any()));
// First reques fails, second request succeeds
- sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2);
+ mCallback.onRegisterServiceSucceeded(key2, request2);
verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2);
int err = 1;
- sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1);
+ mCallback.onRegisterServiceFailed(key1, err);
verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err);
// Client retries first request, it succeeds
manager.registerService(request1, PROTOCOL, listener1);
- int key3 = verifyRequest(NsdManager.REGISTER_SERVICE);
+ int key3 = getRequestKey(req ->
+ verify(mServiceConn, times(3)).registerService(req.capture(), any()));
- sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1);
+ mCallback.onRegisterServiceSucceeded(key3, request1);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1);
// First request is unregistered, it succeeds
manager.unregisterService(listener1);
- int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE);
+ int key3again = getRequestKey(req -> verify(mServiceConn).unregisterService(req.capture()));
assertEquals(key3, key3again);
- sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null);
+ mCallback.onUnregisterServiceSucceeded(key3again);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1);
// Second request is unregistered, it fails
manager.unregisterService(listener2);
- int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE);
+ int key2again = getRequestKey(req ->
+ verify(mServiceConn, times(2)).unregisterService(req.capture()));
assertEquals(key2, key2again);
- sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null);
+ mCallback.onUnregisterServiceFailed(key2again, err);
verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err);
// TODO: do not unregister listener until service is unregistered
@@ -186,8 +254,7 @@
//verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2);
}
- @Test
- public void testDiscoverService() {
+ private void doTestDiscoverService() throws Exception {
NsdManager manager = mManager;
NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
@@ -198,74 +265,77 @@
// Client registers for discovery, request fails
manager.discoverServices("a_type", PROTOCOL, listener);
- int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+ int key1 = getRequestKey(req ->
+ verify(mServiceConn).discoverServices(req.capture(), any()));
int err = 1;
- sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null);
+ mCallback.onDiscoverServicesFailed(key1, err);
verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err);
// Client retries, request succeeds
manager.discoverServices("a_type", PROTOCOL, listener);
- int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+ int key2 = getRequestKey(req ->
+ verify(mServiceConn, times(2)).discoverServices(req.capture(), any()));
- sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1);
+ mCallback.onDiscoverServicesStarted(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
// mdns notifies about services
- sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1);
+ mCallback.onServiceFound(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1);
- sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2);
+ mCallback.onServiceFound(key2, reply2);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2);
- sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2);
+ mCallback.onServiceLost(key2, reply2);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2);
// Client unregisters its listener
manager.stopServiceDiscovery(listener);
- int key2again = verifyRequest(NsdManager.STOP_DISCOVERY);
+ int key2again = getRequestKey(req -> verify(mServiceConn).stopDiscovery(req.capture()));
assertEquals(key2, key2again);
// TODO: unregister listener immediately and stop notifying it about services
// Notifications are still passed to the client's listener
- sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1);
+ mCallback.onServiceLost(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1);
// Client is notified of complete unregistration
- sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type");
+ mCallback.onStopDiscoverySucceeded(key2again);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type");
// Notifications are not passed to the client anymore
- sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3);
+ mCallback.onServiceFound(key2, reply3);
verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3);
// Client registers for service discovery
reset(listener);
manager.discoverServices("a_type", PROTOCOL, listener);
- int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+ int key3 = getRequestKey(req ->
+ verify(mServiceConn, times(3)).discoverServices(req.capture(), any()));
- sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1);
+ mCallback.onDiscoverServicesStarted(key3, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
// Client unregisters immediately, it fails
manager.stopServiceDiscovery(listener);
- int key3again = verifyRequest(NsdManager.STOP_DISCOVERY);
+ int key3again = getRequestKey(req ->
+ verify(mServiceConn, times(2)).stopDiscovery(req.capture()));
assertEquals(key3, key3again);
err = 2;
- sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type");
+ mCallback.onStopDiscoveryFailed(key3again, err);
verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err);
// New notifications are not passed to the client anymore
- sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1);
+ mCallback.onServiceFound(key3, reply1);
verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1);
}
- @Test
- public void testInvalidCalls() {
+ public void doTestInvalidCalls() {
NsdManager manager = mManager;
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
@@ -326,66 +396,10 @@
}
}
- NsdManager makeManager() {
- NsdManager manager = new NsdManager(mContext, mService);
- // Acknowledge first two messages connecting the AsyncChannel.
- verify(mServiceHandler, timeout(mTimeoutMs).times(2)).handleMessage(any());
- reset(mServiceHandler);
- assertNotNull(mServiceHandler.chan);
- return manager;
- }
-
- int verifyRequest(int expectedMessageType) {
- HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs);
- verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any());
- reset(mServiceHandler);
- Message received = mServiceHandler.getLastMessage();
- assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what));
- return received.arg2;
- }
-
- void sendResponse(int replyType, int arg, int key, Object obj) {
- mServiceHandler.chan.sendMessage(replyType, arg, key, obj);
- }
-
- // Implements the server side of AsyncChannel connection protocol
- public static class MockServiceHandler extends Handler {
- public final Context context;
- public AsyncChannel chan;
- public Message lastMessage;
-
- MockServiceHandler(Looper l, Context c) {
- super(l);
- context = c;
- }
-
- synchronized Message getLastMessage() {
- return lastMessage;
- }
-
- synchronized void setLastMessage(Message msg) {
- lastMessage = obtainMessage();
- lastMessage.copyFrom(msg);
- }
-
- @Override
- public void handleMessage(Message msg) {
- setLastMessage(msg);
- if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) {
- chan = new AsyncChannel();
- chan.connect(context, this, msg.replyTo);
- chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
- }
- }
-
- void stop() {
- getLooper().quitSafely();
- }
-
- static MockServiceHandler create(Context context) {
- HandlerThread t = new HandlerThread("mock-service-handler");
- t.start();
- return new MockServiceHandler(t.getLooper(), context);
- }
+ int getRequestKey(ExceptionUtils.ThrowingConsumer<ArgumentCaptor<Integer>> verifier)
+ throws Exception {
+ final ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
+ verifier.accept(captor);
+ return captor.getValue();
}
}
diff --git a/tests/unit/java/com/android/internal/net/VpnProfileTest.java b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
index a945a1f..960a9f1 100644
--- a/tests/unit/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
@@ -16,6 +16,7 @@
package com.android.internal.net;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
@@ -48,6 +49,7 @@
private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
+ private static final int ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE = 25;
@Test
public void testDefaults() throws Exception {
@@ -126,7 +128,12 @@
@Test
public void testParcelUnparcel() {
- assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
+ if (isAtLeastT()) {
+ // excludeLocalRoutes is added in T.
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 24);
+ } else {
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
+ }
}
@Test
@@ -166,7 +173,8 @@
final String tooFewValues =
getEncodedDecodedIkev2ProfileMissingValues(
ENCODED_INDEX_AUTH_PARAMS_INLINE,
- ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
+ ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS,
+ ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE /* missingIndices */);
assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
}
@@ -183,6 +191,17 @@
}
@Test
+ public void testEncodeDecodeMissingExcludeLocalRoutes() {
+ final String tooFewValues =
+ getEncodedDecodedIkev2ProfileMissingValues(
+ ENCODED_INDEX_EXCLUDE_LOCAL_ROUTE /* missingIndices */);
+
+ // Verify decoding without isRestrictedToTestNetworks defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.excludeLocalRoutes);
+ }
+
+ @Test
public void testEncodeDecodeLoginsNotSaved() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
profile.saveLogin = false;
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index d6ce26f..14ff981 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -38,7 +38,6 @@
import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
@@ -52,6 +51,7 @@
import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -82,6 +82,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMTEL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
@@ -151,6 +152,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -158,7 +160,6 @@
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
@@ -249,6 +250,9 @@
import android.net.NetworkStateSnapshot;
import android.net.NetworkTestResultParcelable;
import android.net.OemNetworkPreferences;
+import android.net.PacProxyManager;
+import android.net.ProfileNetworkPreference;
+import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
import android.net.QosFilter;
@@ -313,23 +317,28 @@
import androidx.test.filters.SmallTest;
import com.android.connectivity.resources.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.ConnectivityService.NetworkRequestInfo;
+import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
+import com.android.server.connectivity.ConnectivityFlags;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
+import com.android.server.connectivity.UidRangeUtils;
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.VpnProfileStore;
import com.android.server.net.NetworkPinner;
@@ -342,6 +351,7 @@
import com.android.testutils.TestableNetworkOfferCallback;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -435,6 +445,10 @@
private static final int TEST_WORK_PROFILE_USER_ID = 2;
private static final int TEST_WORK_PROFILE_APP_UID =
UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID);
+ private static final int TEST_APP_ID_2 = 104;
+ private static final int TEST_WORK_PROFILE_APP_UID_2 =
+ UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID_2);
+
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String CLAT_MOBILE_IFNAME = CLAT_PREFIX + MOBILE_IFNAME;
@@ -464,7 +478,7 @@
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
private HandlerThread mVMSHandlerThread;
- private ConnectivityService.Dependencies mDeps;
+ private ConnectivityServiceDependencies mDeps;
private ConnectivityService mService;
private WrappedConnectivityManager mCm;
private TestNetworkAgentWrapper mWiFiNetworkAgent;
@@ -474,6 +488,7 @@
private Context mContext;
private NetworkPolicyCallback mPolicyCallback;
private WrappedMultinetworkPolicyTracker mPolicyTracker;
+ private ProxyTracker mProxyTracker;
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
private QosCallbackMockHelper mQosCallbackMockHelper;
@@ -483,6 +498,8 @@
private TestNetworkCallback mSystemDefaultNetworkCallback;
private TestNetworkCallback mProfileDefaultNetworkCallback;
private TestNetworkCallback mTestPackageDefaultNetworkCallback;
+ private TestNetworkCallback mProfileDefaultNetworkCallbackAsAppUid2;
+ private TestNetworkCallback mTestPackageDefaultNetworkCallback2;
// State variables required to emulate NetworkPolicyManagerService behaviour.
private int mBlockedReasons = BLOCKED_REASON_NONE;
@@ -502,13 +519,12 @@
@Mock LocationManager mLocationManager;
@Mock AppOpsManager mAppOpsManager;
@Mock TelephonyManager mTelephonyManager;
- @Mock MockableSystemProperties mSystemProperties;
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
@Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
@Mock Resources mResources;
- @Mock ProxyTracker mProxyTracker;
+ @Mock PacProxyManager mPacProxyManager;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -554,13 +570,13 @@
super(base);
mInternalResources = spy(base.getResources());
- when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes))
- .thenReturn(new String[] {
- "wifi,1,1,1,-1,true",
- "mobile,0,0,0,-1,true",
- "mobile_mms,2,0,2,60000,true",
- "mobile_supl,3,0,2,60000,true",
- });
+ doReturn(new String[] {
+ "wifi,1,1,1,-1,true",
+ "mobile,0,0,0,-1,true",
+ "mobile_mms,2,0,2,60000,true",
+ "mobile_supl,3,0,2,60000,true",
+ }).when(mInternalResources)
+ .getStringArray(com.android.internal.R.array.networkAttributes);
mContentResolver = new MockContentResolver();
mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
@@ -610,6 +626,7 @@
if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager;
if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
if (Context.BATTERY_STATS_SERVICE.equals(name)) return mBatteryStatsManager;
+ if (Context.PAC_PROXY_SERVICE.equals(name)) return mPacProxyManager;
return super.getSystemService(name);
}
@@ -1577,11 +1594,11 @@
}
private <T> T doAsUid(final int uid, @NonNull final Supplier<T> what) {
- doReturn(uid).when(mDeps).getCallingUid();
+ mDeps.setCallingUid(uid);
try {
return what.get();
} finally {
- returnRealCallingUid();
+ mDeps.setCallingUid(null);
}
}
@@ -1658,21 +1675,21 @@
MockitoAnnotations.initMocks(this);
- when(mUserManager.getAliveUsers()).thenReturn(asList(PRIMARY_USER_INFO));
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(asList(PRIMARY_USER_HANDLE));
- when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO);
+ doReturn(asList(PRIMARY_USER_INFO)).when(mUserManager).getAliveUsers();
+ doReturn(asList(PRIMARY_USER_HANDLE)).when(mUserManager).getUserHandles(anyBoolean());
+ doReturn(PRIMARY_USER_INFO).when(mUserManager).getUserInfo(PRIMARY_USER);
// canHaveRestrictedProfile does not take a userId. It applies to the userId of the context
// it was started from, i.e., PRIMARY_USER.
- when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
- when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO);
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ doReturn(RESTRICTED_USER_INFO).when(mUserManager).getUserInfo(RESTRICTED_USER);
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
- .thenReturn(applicationInfo);
- when(mPackageManager.getTargetSdkVersion(anyString()))
- .thenReturn(applicationInfo.targetSdkVersion);
- when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
+ doReturn(applicationInfo).when(mPackageManager)
+ .getApplicationInfoAsUser(anyString(), anyInt(), any());
+ doReturn(applicationInfo.targetSdkVersion).when(mPackageManager)
+ .getTargetSdkVersion(anyString());
+ doReturn(new int[0]).when(mSystemConfigManager).getSystemPermissionUids(anyString());
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
@@ -1700,8 +1717,15 @@
mCsHandlerThread = new HandlerThread("TestConnectivityService");
mVMSHandlerThread = new HandlerThread("TestVpnManagerService");
- mDeps = makeDependencies();
- returnRealCallingUid();
+ mProxyTracker = new ProxyTracker(mServiceContext, mock(Handler.class),
+ 16 /* EVENT_PROXY_HAS_CHANGED */);
+
+ initMockedResources();
+ final Context mockResContext = mock(Context.class);
+ doReturn(mResources).when(mockResContext).getResources();
+ ConnectivityResources.setResourcesContextForTest(mockResContext);
+ mDeps = new ConnectivityServiceDependencies(mockResContext);
+
mService = new ConnectivityService(mServiceContext,
mMockDnsResolver,
mock(IpConnectivityLog.class),
@@ -1709,7 +1733,6 @@
mDeps);
mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
- verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<NetworkPolicyCallback> policyCallbackCaptor =
ArgumentCaptor.forClass(NetworkPolicyCallback.class);
@@ -1733,33 +1756,7 @@
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
}
- private void returnRealCallingUid() {
- doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid();
- }
-
- private ConnectivityService.Dependencies makeDependencies() {
- doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
- final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
- doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
- doReturn(mNetIdManager).when(deps).makeNetIdManager();
- doReturn(mNetworkStack).when(deps).getNetworkStack();
- doReturn(mSystemProperties).when(deps).getSystemProperties();
- doReturn(mProxyTracker).when(deps).makeProxyTracker(any(), any());
- doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any());
- doAnswer(inv -> {
- mPolicyTracker = new WrappedMultinetworkPolicyTracker(
- inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
- return mPolicyTracker;
- }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
- doReturn(true).when(deps).getCellular464XlatEnabled();
- doAnswer(inv ->
- new LocationPermissionChecker(inv.getArgument(0)) {
- @Override
- protected int getCurrentUser() {
- return runAsShell(CREATE_USERS, () -> super.getCurrentUser());
- }
- }).when(deps).makeLocationPermissionChecker(any());
-
+ private void initMockedResources() {
doReturn(60000).when(mResources).getInteger(R.integer.config_networkTransitionTimeout);
doReturn("").when(mResources).getString(R.string.config_networkCaptivePortalServerUrl);
doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray(
@@ -1772,7 +1769,8 @@
R.array.config_protectedNetworks);
// We don't test the actual notification value strings, so just return an empty array.
// It doesn't matter what the values are as long as it's not null.
- doReturn(new String[0]).when(mResources).getStringArray(R.array.network_switch_type_name);
+ doReturn(new String[0]).when(mResources)
+ .getStringArray(R.array.network_switch_type_name);
doReturn(R.array.config_networkSupportedKeepaliveCount).when(mResources)
.getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any());
@@ -1781,22 +1779,170 @@
doReturn(R.integer.config_networkAvoidBadWifi).when(mResources)
.getIdentifier(eq("config_networkAvoidBadWifi"), eq("integer"), any());
doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ doReturn(true).when(mResources)
+ .getBoolean(R.bool.config_cellular_radio_timesharing_capable);
+ }
- final ConnectivityResources connRes = mock(ConnectivityResources.class);
- doReturn(mResources).when(connRes).get();
- doReturn(connRes).when(deps).getResources(any());
+ class ConnectivityServiceDependencies extends ConnectivityService.Dependencies {
+ final ConnectivityResources mConnRes;
+ @Mock final MockableSystemProperties mSystemProperties;
- final Context mockResContext = mock(Context.class);
- doReturn(mResources).when(mockResContext).getResources();
- ConnectivityResources.setResourcesContextForTest(mockResContext);
+ ConnectivityServiceDependencies(final Context mockResContext) {
+ mSystemProperties = mock(MockableSystemProperties.class);
+ doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
- doAnswer(inv -> {
- final PendingIntent a = inv.getArgument(0);
- final PendingIntent b = inv.getArgument(1);
+ mConnRes = new ConnectivityResources(mockResContext);
+ }
+
+ @Override
+ public MockableSystemProperties getSystemProperties() {
+ return mSystemProperties;
+ }
+
+ @Override
+ public HandlerThread makeHandlerThread() {
+ return mCsHandlerThread;
+ }
+
+ @Override
+ public NetworkStackClientBase getNetworkStack() {
+ return mNetworkStack;
+ }
+
+ @Override
+ public ProxyTracker makeProxyTracker(final Context context, final Handler handler) {
+ return mProxyTracker;
+ }
+
+ @Override
+ public NetIdManager makeNetIdManager() {
+ return mNetIdManager;
+ }
+
+ @Override
+ public boolean queryUserAccess(final int uid, final Network network,
+ final ConnectivityService cs) {
+ return true;
+ }
+
+ @Override
+ public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(final Context c,
+ final Handler h, final Runnable r) {
+ if (null != mPolicyTracker) {
+ throw new IllegalStateException("Multinetwork policy tracker already initialized");
+ }
+ mPolicyTracker = new WrappedMultinetworkPolicyTracker(mServiceContext, h, r);
+ return mPolicyTracker;
+ }
+
+ @Override
+ public ConnectivityResources getResources(final Context ctx) {
+ return mConnRes;
+ }
+
+ @Override
+ public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
+ return new LocationPermissionChecker(context) {
+ @Override
+ protected int getCurrentUser() {
+ return runAsShell(CREATE_USERS, () -> super.getCurrentUser());
+ }
+ };
+ }
+
+ @Override
+ public boolean intentFilterEquals(final PendingIntent a, final PendingIntent b) {
return runAsShell(GET_INTENT_SENDER_INTENT, () -> a.intentFilterEquals(b));
- }).when(deps).intentFilterEquals(any(), any());
+ }
- return deps;
+ @GuardedBy("this")
+ private Integer mCallingUid = null;
+
+ @Override
+ public int getCallingUid() {
+ synchronized (this) {
+ if (null != mCallingUid) return mCallingUid;
+ return super.getCallingUid();
+ }
+ }
+
+ // Pass null for the real calling UID
+ public void setCallingUid(final Integer uid) {
+ synchronized (this) {
+ mCallingUid = uid;
+ }
+ }
+
+ @GuardedBy("this")
+ private boolean mCellular464XlatEnabled = true;
+
+ @Override
+ public boolean getCellular464XlatEnabled() {
+ synchronized (this) {
+ return mCellular464XlatEnabled;
+ }
+ }
+
+ public void setCellular464XlatEnabled(final boolean enabled) {
+ synchronized (this) {
+ mCellular464XlatEnabled = enabled;
+ }
+ }
+
+ @GuardedBy("this")
+ private Integer mConnectionOwnerUid = null;
+
+ @Override
+ public int getConnectionOwnerUid(final int protocol, final InetSocketAddress local,
+ final InetSocketAddress remote) {
+ synchronized (this) {
+ if (null != mConnectionOwnerUid) return mConnectionOwnerUid;
+ return super.getConnectionOwnerUid(protocol, local, remote);
+ }
+ }
+
+ // Pass null to get the production implementation of getConnectionOwnerUid
+ public void setConnectionOwnerUid(final Integer uid) {
+ synchronized (this) {
+ mConnectionOwnerUid = uid;
+ }
+ }
+
+ final class ReportedInterfaces {
+ public final Context context;
+ public final String iface;
+ public final int[] transportTypes;
+ ReportedInterfaces(final Context c, final String i, final int[] t) {
+ context = c;
+ iface = i;
+ transportTypes = t;
+ }
+
+ public boolean contentEquals(final Context c, final String i, final int[] t) {
+ return Objects.equals(context, c) && Objects.equals(iface, i)
+ && Arrays.equals(transportTypes, t);
+ }
+ }
+
+ final ArrayTrackRecord<ReportedInterfaces> mReportedInterfaceHistory =
+ new ArrayTrackRecord<>();
+
+ @Override
+ public void reportNetworkInterfaceForTransports(final Context context, final String iface,
+ final int[] transportTypes) {
+ mReportedInterfaceHistory.add(new ReportedInterfaces(context, iface, transportTypes));
+ super.reportNetworkInterfaceForTransports(context, iface, transportTypes);
+ }
+
+ @Override
+ public boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled) {
+ switch (name) {
+ case ConnectivityFlags.NO_REMATCH_ALL_REQUESTS_ON_REGISTER:
+ return true;
+ default:
+ return super.isFeatureEnabled(context, name, defaultEnabled);
+ }
+ }
}
private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
@@ -1861,36 +2007,34 @@
final String myPackageName = mContext.getPackageName();
final PackageInfo myPackageInfo = mContext.getPackageManager().getPackageInfo(
myPackageName, PackageManager.GET_PERMISSIONS);
- when(mPackageManager.getPackagesForUid(Binder.getCallingUid())).thenReturn(
- new String[] {myPackageName});
- when(mPackageManager.getPackageInfoAsUser(eq(myPackageName), anyInt(),
- eq(UserHandle.getCallingUserId()))).thenReturn(myPackageInfo);
+ doReturn(new String[] {myPackageName}).when(mPackageManager)
+ .getPackagesForUid(Binder.getCallingUid());
+ doReturn(myPackageInfo).when(mPackageManager).getPackageInfoAsUser(
+ eq(myPackageName), anyInt(), eq(UserHandle.getCallingUserId()));
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- asList(new PackageInfo[] {
- buildPackageInfo(/* SYSTEM */ false, APP1_UID),
- buildPackageInfo(/* SYSTEM */ false, APP2_UID),
- buildPackageInfo(/* SYSTEM */ false, VPN_UID)
- }));
+ doReturn(asList(new PackageInfo[] {
+ buildPackageInfo(/* SYSTEM */ false, APP1_UID),
+ buildPackageInfo(/* SYSTEM */ false, APP2_UID),
+ buildPackageInfo(/* SYSTEM */ false, VPN_UID)
+ })).when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
// Create a fake always-on VPN package.
final int userId = UserHandle.getCallingUserId();
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; // Always-on supported in N+.
- when(mPackageManager.getApplicationInfoAsUser(eq(ALWAYS_ON_PACKAGE), anyInt(),
- eq(userId))).thenReturn(applicationInfo);
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfoAsUser(
+ eq(ALWAYS_ON_PACKAGE), anyInt(), eq(userId));
// Minimal mocking to keep Vpn#isAlwaysOnPackageSupported happy.
ResolveInfo rInfo = new ResolveInfo();
rInfo.serviceInfo = new ServiceInfo();
rInfo.serviceInfo.metaData = new Bundle();
final List<ResolveInfo> services = asList(new ResolveInfo[]{rInfo});
- when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
- eq(userId))).thenReturn(services);
- when(mPackageManager.getPackageUidAsUser(TEST_PACKAGE_NAME, userId))
- .thenReturn(Process.myUid());
- when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, userId))
- .thenReturn(VPN_UID);
+ doReturn(services).when(mPackageManager).queryIntentServicesAsUser(
+ any(), eq(PackageManager.GET_META_DATA), eq(userId));
+ doReturn(Process.myUid()).when(mPackageManager).getPackageUidAsUser(
+ TEST_PACKAGE_NAME, userId);
+ doReturn(VPN_UID).when(mPackageManager).getPackageUidAsUser(ALWAYS_ON_PACKAGE, userId);
}
private void verifyActiveNetwork(int transport) {
@@ -2007,6 +2151,39 @@
return expected;
}
+ private ExpectedBroadcast expectProxyChangeAction(ProxyInfo proxy) {
+ return registerPacProxyBroadcastThat(intent -> {
+ final ProxyInfo actualProxy = (ProxyInfo) intent.getExtra(Proxy.EXTRA_PROXY_INFO,
+ ProxyInfo.buildPacProxy(Uri.EMPTY));
+ return proxy.equals(actualProxy);
+ });
+ }
+
+ private ExpectedBroadcast registerPacProxyBroadcast() {
+ return registerPacProxyBroadcastThat(intent -> true);
+ }
+
+ private ExpectedBroadcast registerPacProxyBroadcastThat(
+ @NonNull final Predicate<Intent> filter) {
+ final IntentFilter intentFilter = new IntentFilter(Proxy.PROXY_CHANGE_ACTION);
+ // AtomicReference allows receiver to access expected even though it is constructed later.
+ final AtomicReference<ExpectedBroadcast> expectedRef = new AtomicReference<>();
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ final ProxyInfo proxy = (ProxyInfo) intent.getExtra(
+ Proxy.EXTRA_PROXY_INFO, ProxyInfo.buildPacProxy(Uri.EMPTY));
+ Log.d(TAG, "Receive PROXY_CHANGE_ACTION, proxy = " + proxy);
+ if (filter.test(intent)) {
+ expectedRef.get().complete(intent);
+ }
+ }
+ };
+ final ExpectedBroadcast expected = new ExpectedBroadcast(receiver);
+ expectedRef.set(expected);
+ mServiceContext.registerReceiver(receiver, intentFilter);
+ return expected;
+ }
+
private boolean extraInfoInBroadcastHasExpectedNullness(NetworkInfo ni) {
final DetailedState state = ni.getDetailedState();
if (state == DetailedState.CONNECTED && ni.getExtraInfo() == null) return false;
@@ -2262,8 +2439,22 @@
// After waitForIdle(), the message was processed and the service didn't crash.
}
+ // TODO : migrate to @Parameterized
@Test
- public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
+ public void testValidatedCellularOutscoresUnvalidatedWiFi_CanTimeShare() throws Exception {
+ // The behavior of this test should be the same whether the radio can time share or not.
+ doTestValidatedCellularOutscoresUnvalidatedWiFi(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testValidatedCellularOutscoresUnvalidatedWiFi_CannotTimeShare() throws Exception {
+ doTestValidatedCellularOutscoresUnvalidatedWiFi(false);
+ }
+
+ public void doTestValidatedCellularOutscoresUnvalidatedWiFi(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2297,8 +2488,21 @@
verifyNoNetwork();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
+ public void testUnvalidatedWifiOutscoresUnvalidatedCellular_CanTimeShare() throws Exception {
+ doTestUnvalidatedWifiOutscoresUnvalidatedCellular(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testUnvalidatedWifiOutscoresUnvalidatedCellular_CannotTimeShare() throws Exception {
+ doTestUnvalidatedWifiOutscoresUnvalidatedCellular(false);
+ }
+
+ public void doTestUnvalidatedWifiOutscoresUnvalidatedCellular(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2323,8 +2527,21 @@
verifyNoNetwork();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testUnlingeringDoesNotValidate() throws Exception {
+ public void testUnlingeringDoesNotValidate_CanTimeShare() throws Exception {
+ doTestUnlingeringDoesNotValidate(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testUnlingeringDoesNotValidate_CannotTimeShare() throws Exception {
+ doTestUnlingeringDoesNotValidate(false);
+ }
+
+ public void doTestUnlingeringDoesNotValidate(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2351,8 +2568,134 @@
NET_CAPABILITY_VALIDATED));
}
+ // TODO : migrate to @Parameterized
@Test
- public void testCellularOutscoresWeakWifi() throws Exception {
+ public void testRequestMigrationToSameTransport_CanTimeShare() throws Exception {
+ // Simulate a device where the cell radio is capable of time sharing
+ mService.mCellularRadioTimesharingCapable = true;
+ doTestRequestMigrationToSameTransport(TRANSPORT_CELLULAR, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_WIFI, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_ETHERNET, true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testRequestMigrationToSameTransport_CannotTimeShare() throws Exception {
+ // Simulate a device where the cell radio is not capable of time sharing
+ mService.mCellularRadioTimesharingCapable = false;
+ doTestRequestMigrationToSameTransport(TRANSPORT_CELLULAR, false);
+ doTestRequestMigrationToSameTransport(TRANSPORT_WIFI, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_ETHERNET, true);
+ }
+
+ public void doTestRequestMigrationToSameTransport(final int transport,
+ final boolean expectLingering) throws Exception {
+ // To speed up tests the linger delay is very short by default in tests but this
+ // test needs to make sure the delay is not incurred so a longer value is safer (it
+ // reduces the risk that a bug exists but goes undetected). The alarm manager in the test
+ // throws and crashes CS if this is set to anything more than the below constant though.
+ mService.mLingerDelayMs = UNREASONABLY_LONG_ALARM_WAIT_MS;
+
+ final TestNetworkCallback generalCb = new TestNetworkCallback();
+ final TestNetworkCallback defaultCb = new TestNetworkCallback();
+ mCm.registerNetworkCallback(
+ new NetworkRequest.Builder().addTransportType(transport | transport).build(),
+ generalCb);
+ mCm.registerDefaultNetworkCallback(defaultCb);
+
+ // Bring up net agent 1
+ final TestNetworkAgentWrapper net1 = new TestNetworkAgentWrapper(transport);
+ net1.connect(true);
+ // Make sure the default request is on net 1
+ generalCb.expectAvailableThenValidatedCallbacks(net1);
+ defaultCb.expectAvailableThenValidatedCallbacks(net1);
+
+ // Bring up net 2 with primary and mms
+ final TestNetworkAgentWrapper net2 = new TestNetworkAgentWrapper(transport);
+ net2.addCapability(NET_CAPABILITY_MMS);
+ net2.setScore(new NetworkScore.Builder().setTransportPrimary(true).build());
+ net2.connect(true);
+
+ // Make sure the default request goes to net 2
+ generalCb.expectAvailableCallbacksUnvalidated(net2);
+ if (expectLingering) {
+ generalCb.expectCallback(CallbackEntry.LOSING, net1);
+ }
+ generalCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, net2);
+ defaultCb.expectAvailableDoubleValidatedCallbacks(net2);
+
+ // Make sure cell 1 is unwanted immediately if the radio can't time share, but only
+ // after some delay if it can.
+ if (expectLingering) {
+ net1.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS); // always incurs the timeout
+ generalCb.assertNoCallback();
+ // assertNotDisconnected waited for TEST_CALLBACK_TIMEOUT_MS, so waiting for the
+ // linger period gives TEST_CALLBACK_TIMEOUT_MS time for the event to process.
+ net1.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
+ } else {
+ net1.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ }
+ net1.disconnect();
+ generalCb.expectCallback(CallbackEntry.LOST, net1);
+
+ // Remove primary from net 2
+ net2.setScore(new NetworkScore.Builder().build());
+ // Request MMS
+ final TestNetworkCallback mmsCallback = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+ mmsCallback);
+ mmsCallback.expectAvailableCallbacksValidated(net2);
+
+ // Bring up net 3 with primary but without MMS
+ final TestNetworkAgentWrapper net3 = new TestNetworkAgentWrapper(transport);
+ net3.setScore(new NetworkScore.Builder().setTransportPrimary(true).build());
+ net3.connect(true);
+
+ // Make sure default goes to net 3, but the MMS request doesn't
+ generalCb.expectAvailableThenValidatedCallbacks(net3);
+ defaultCb.expectAvailableDoubleValidatedCallbacks(net3);
+ mmsCallback.assertNoCallback();
+ net2.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS); // Always incurs the timeout
+
+ // Revoke MMS request and make sure net 2 is torn down with the appropriate delay
+ mCm.unregisterNetworkCallback(mmsCallback);
+ if (expectLingering) {
+ // If the radio can time share, the linger delay hasn't elapsed yet, so apps will
+ // get LOSING. If the radio can't time share, this is a hard loss, since the last
+ // request keeping up this network has been removed and the network isn't lingering
+ // for any other request.
+ generalCb.expectCallback(CallbackEntry.LOSING, net2);
+ net2.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ generalCb.assertNoCallback();
+ net2.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
+ } else {
+ net2.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ }
+ net2.disconnect();
+ generalCb.expectCallback(CallbackEntry.LOST, net2);
+ defaultCb.assertNoCallback();
+
+ net3.disconnect();
+ mCm.unregisterNetworkCallback(defaultCb);
+ mCm.unregisterNetworkCallback(generalCb);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularOutscoresWeakWifi_CanTimeShare() throws Exception {
+ // The behavior of this test should be the same whether the radio can time share or not.
+ doTestCellularOutscoresWeakWifi(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularOutscoresWeakWifi_CannotTimeShare() throws Exception {
+ doTestCellularOutscoresWeakWifi(false);
+ }
+
+ public void doTestCellularOutscoresWeakWifi(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2377,8 +2720,21 @@
verifyActiveNetwork(TRANSPORT_WIFI);
}
+ // TODO : migrate to @Parameterized
@Test
- public void testReapingNetwork() throws Exception {
+ public void testReapingNetwork_CanTimeShare() throws Exception {
+ doTestReapingNetwork(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testReapingNetwork_CannotTimeShare() throws Exception {
+ doTestReapingNetwork(false);
+ }
+
+ public void doTestReapingNetwork(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up WiFi without NET_CAPABILITY_INTERNET.
// Expect it to be torn down immediately because it satisfies no requests.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -2406,8 +2762,21 @@
mWiFiNetworkAgent.expectDisconnected();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testCellularFallback() throws Exception {
+ public void testCellularFallback_CanTimeShare() throws Exception {
+ doTestCellularFallback(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularFallback_CannotTimeShare() throws Exception {
+ doTestCellularFallback(false);
+ }
+
+ public void doTestCellularFallback(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2444,8 +2813,21 @@
verifyActiveNetwork(TRANSPORT_WIFI);
}
+ // TODO : migrate to @Parameterized
@Test
- public void testWiFiFallback() throws Exception {
+ public void testWiFiFallback_CanTimeShare() throws Exception {
+ doTestWiFiFallback(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testWiFiFallback_CannotTimeShare() throws Exception {
+ doTestWiFiFallback(false);
+ }
+
+ public void doTestWiFiFallback(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2961,9 +3343,8 @@
private void grantUsingBackgroundNetworksPermissionForUid(
final int uid, final String packageName) throws Exception {
- when(mPackageManager.getPackageInfo(
- eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER)))
- .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid));
+ doReturn(buildPackageInfo(true /* hasSystemPermission */, uid)).when(mPackageManager)
+ .getPackageInfo(eq(packageName), eq(GET_PERMISSIONS));
mService.mPermissionMonitor.onPackageAdded(packageName, uid);
}
@@ -3015,12 +3396,12 @@
private NativeNetworkConfig nativeNetworkConfigPhysical(int netId, int permission) {
return new NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL, permission,
- /*secure=*/ false, VpnManager.TYPE_VPN_NONE);
+ /*secure=*/ false, VpnManager.TYPE_VPN_NONE, /*excludeLocalRoutes=*/ false);
}
private NativeNetworkConfig nativeNetworkConfigVpn(int netId, boolean secure, int vpnType) {
return new NativeNetworkConfig(netId, NativeNetworkType.VIRTUAL, INetd.PERMISSION_NONE,
- secure, vpnType);
+ secure, vpnType, /*excludeLocalRoutes=*/ false);
}
@Test
@@ -3216,7 +3597,7 @@
|| capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS
|| capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP
|| capability == NET_CAPABILITY_VSIM || capability == NET_CAPABILITY_BIP
- || capability == NET_CAPABILITY_ENTERPRISE) {
+ || capability == NET_CAPABILITY_ENTERPRISE || capability == NET_CAPABILITY_MMTEL) {
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
} else {
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
@@ -3344,6 +3725,7 @@
tryNetworkFactoryRequests(NET_CAPABILITY_WIFI_P2P);
tryNetworkFactoryRequests(NET_CAPABILITY_IA);
tryNetworkFactoryRequests(NET_CAPABILITY_RCS);
+ tryNetworkFactoryRequests(NET_CAPABILITY_MMTEL);
tryNetworkFactoryRequests(NET_CAPABILITY_XCAP);
tryNetworkFactoryRequests(NET_CAPABILITY_ENTERPRISE);
tryNetworkFactoryRequests(NET_CAPABILITY_EIMS);
@@ -4190,7 +4572,7 @@
* @return the context's attribution tag
*/
private String getAttributionTag() {
- return null;
+ return mContext.getAttributionTag();
}
@Test
@@ -4457,6 +4839,124 @@
mCm.unregisterNetworkCallback(callback);
}
+ @Test
+ public void testNetworkCallbackWithNullUids() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ // Attempt to file a callback for networks applying to another UID. This does not actually
+ // work, because this code does not currently have permission to do so. The callback behaves
+ // exactly the same as the one registered just above.
+ final int otherUid = UserHandle.getUid(RESTRICTED_USER, VPN_UID);
+ final NetworkRequest otherUidRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .setUids(UidRange.toIntRanges(uidRangesForUids(otherUid)))
+ .build();
+ final TestNetworkCallback otherUidCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(otherUidRequest, otherUidCallback);
+
+ final NetworkRequest includeOtherUidsRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .setIncludeOtherUidNetworks(true)
+ .build();
+ final TestNetworkCallback includeOtherUidsCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(includeOtherUidsRequest, includeOtherUidsCallback);
+
+ // Both callbacks see a network with no specifier that applies to their UID.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ otherUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ includeOtherUidsCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ otherUidCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ includeOtherUidsCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // Only the includeOtherUidsCallback sees a VPN that does not apply to its UID.
+ final UidRange range = UidRange.createForUser(UserHandle.of(RESTRICTED_USER));
+ final Set<UidRange> vpnRanges = Collections.singleton(range);
+ mMockVpn.establish(new LinkProperties(), VPN_UID, vpnRanges);
+ includeOtherUidsCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ callback.assertNoCallback();
+ otherUidCallback.assertNoCallback();
+
+ mMockVpn.disconnect();
+ includeOtherUidsCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ callback.assertNoCallback();
+ otherUidCallback.assertNoCallback();
+ }
+
+ private static class RedactableNetworkSpecifier extends NetworkSpecifier {
+ public static final int ID_INVALID = -1;
+
+ public final int networkId;
+
+ RedactableNetworkSpecifier(int networkId) {
+ this.networkId = networkId;
+ }
+
+ @Override
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+ return other instanceof RedactableNetworkSpecifier
+ && this.networkId == ((RedactableNetworkSpecifier) other).networkId;
+ }
+
+ @Override
+ public NetworkSpecifier redact() {
+ return new RedactableNetworkSpecifier(ID_INVALID);
+ }
+ }
+
+ @Test
+ public void testNetworkCallbackWithNullUidsRedactsSpecifier() throws Exception {
+ final RedactableNetworkSpecifier specifier = new RedactableNetworkSpecifier(42);
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ // Attempt to file a callback for networks applying to another UID. This does not actually
+ // work, because this code does not currently have permission to do so. The callback behaves
+ // exactly the same as the one registered just above.
+ final int otherUid = UserHandle.getUid(RESTRICTED_USER, VPN_UID);
+ final NetworkRequest otherUidRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .setUids(UidRange.toIntRanges(uidRangesForUids(otherUid)))
+ .build();
+ final TestNetworkCallback otherUidCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(otherUidRequest, otherUidCallback);
+
+ final NetworkRequest includeOtherUidsRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .setIncludeOtherUidNetworks(true)
+ .build();
+ final TestNetworkCallback includeOtherUidsCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(includeOtherUidsRequest, callback);
+
+ // Only the regular callback sees the network, because callbacks filed with no UID have
+ // their specifiers redacted.
+ final LinkProperties emptyLp = new LinkProperties();
+ final NetworkCapabilities ncTemplate = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, emptyLp, ncTemplate);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ otherUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ includeOtherUidsCallback.assertNoCallback();
+ }
+
private void setCaptivePortalMode(int mode) {
ContentResolver cr = mServiceContext.getContentResolver();
Settings.Global.putInt(cr, ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE, mode);
@@ -4801,9 +5301,6 @@
@Test
public void testAvoidBadWifiSetting() throws Exception {
- final ContentResolver cr = mServiceContext.getContentResolver();
- final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
-
doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
testAvoidBadWifiConfig_ignoreSettings();
@@ -6237,16 +6734,16 @@
mCm.unregisterNetworkCallback(networkCallback);
}
- private void expectNotifyNetworkStatus(List<Network> networks, String defaultIface,
+ private void expectNotifyNetworkStatus(List<Network> defaultNetworks, String defaultIface,
Integer vpnUid, String vpnIfname, List<String> underlyingIfaces) throws Exception {
- ArgumentCaptor<List<Network>> networksCaptor = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<List<Network>> defaultNetworksCaptor = ArgumentCaptor.forClass(List.class);
ArgumentCaptor<List<UnderlyingNetworkInfo>> vpnInfosCaptor =
ArgumentCaptor.forClass(List.class);
- verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(),
+ verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(defaultNetworksCaptor.capture(),
any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
- assertSameElements(networks, networksCaptor.getValue());
+ assertSameElements(defaultNetworks, defaultNetworksCaptor.getValue());
List<UnderlyingNetworkInfo> infos = vpnInfosCaptor.getValue();
if (vpnUid != null) {
@@ -6262,8 +6759,8 @@
}
private void expectNotifyNetworkStatus(
- List<Network> networks, String defaultIface) throws Exception {
- expectNotifyNetworkStatus(networks, defaultIface, null, null, List.of());
+ List<Network> defaultNetworks, String defaultIface) throws Exception {
+ expectNotifyNetworkStatus(defaultNetworks, defaultIface, null, null, List.of());
}
@Test
@@ -6919,6 +7416,15 @@
initialCaps.addTransportType(TRANSPORT_VPN);
initialCaps.addCapability(NET_CAPABILITY_INTERNET);
initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);
+ final ArrayList<Network> emptyUnderlyingNetworks = new ArrayList<Network>();
+ final ArrayList<Network> underlyingNetworksContainMobile = new ArrayList<Network>();
+ underlyingNetworksContainMobile.add(mobile);
+ final ArrayList<Network> underlyingNetworksContainWifi = new ArrayList<Network>();
+ underlyingNetworksContainWifi.add(wifi);
+ final ArrayList<Network> underlyingNetworksContainMobileAndMobile =
+ new ArrayList<Network>();
+ underlyingNetworksContainMobileAndMobile.add(mobile);
+ underlyingNetworksContainMobileAndMobile.add(wifi);
final NetworkCapabilities withNoUnderlying = new NetworkCapabilities();
withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET);
@@ -6927,17 +7433,20 @@
withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
withNoUnderlying.addTransportType(TRANSPORT_VPN);
withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN);
+ withNoUnderlying.setUnderlyingNetworks(emptyUnderlyingNetworks);
final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying);
withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR);
withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
withMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
+ withMobileUnderlying.setUnderlyingNetworks(underlyingNetworksContainMobile);
final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying);
withWifiUnderlying.addTransportType(TRANSPORT_WIFI);
withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
withWifiUnderlying.setLinkUpstreamBandwidthKbps(20);
+ withWifiUnderlying.setUnderlyingNetworks(underlyingNetworksContainWifi);
final NetworkCapabilities withWifiAndMobileUnderlying =
new NetworkCapabilities(withNoUnderlying);
@@ -6947,6 +7456,7 @@
withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);
+ withWifiAndMobileUnderlying.setUnderlyingNetworks(underlyingNetworksContainMobileAndMobile);
final NetworkCapabilities initialCapsNotMetered = new NetworkCapabilities(initialCaps);
initialCapsNotMetered.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -6954,40 +7464,61 @@
NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{}, initialCapsNotMetered, caps);
assertEquals(withNoUnderlying, caps);
+ assertEquals(0, new ArrayList<>(caps.getUnderlyingNetworks()).size());
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null}, initialCapsNotMetered, caps);
assertEquals(withNoUnderlying, caps);
+ assertEquals(0, new ArrayList<>(caps.getUnderlyingNetworks()).size());
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{mobile}, initialCapsNotMetered, caps);
assertEquals(withMobileUnderlying, caps);
+ assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
+ caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCapsNotMetered, caps);
assertEquals(withWifiUnderlying, caps);
+ assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCaps, caps);
assertEquals(withWifiUnderlying, caps);
+ assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, initialCaps, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
+ assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
initialCapsNotMetered, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
+ assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
initialCapsNotMetered, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
+ assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
+ caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(null, initialCapsNotMetered, caps);
assertEquals(withWifiUnderlying, caps);
+ assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
}
@Test
@@ -6996,51 +7527,78 @@
final NetworkRequest request = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN).build();
- mCm.registerNetworkCallback(request, callback);
+ runAsShell(NETWORK_SETTINGS, () -> {
+ mCm.registerNetworkCallback(request, callback);
- // Bring up a VPN that specifies an underlying network that does not exist yet.
- // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
- // (and doing so is difficult without using reflection) but it's good to test that the code
- // behaves approximately correctly.
- mMockVpn.establishForMyUid(false, true, false);
- assertUidRangesUpdatedForMyUid(true);
- final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
- mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
- callback.expectAvailableCallbacksUnvalidated(mMockVpn);
- assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasTransport(TRANSPORT_VPN));
- assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasTransport(TRANSPORT_WIFI));
+ // Bring up a VPN that specifies an underlying network that does not exist yet.
+ // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist
+ // yet, (and doing so is difficult without using reflection) but it's good to test that
+ // the code behaves approximately correctly.
+ mMockVpn.establishForMyUid(false, true, false);
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+ assertUidRangesUpdatedForMyUid(true);
+ final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
+ mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
+ // onCapabilitiesChanged() should be called because
+ // NetworkCapabilities#mUnderlyingNetworks is updated.
+ CallbackEntry ce = callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
+ mMockVpn);
+ final NetworkCapabilities vpnNc1 = ((CallbackEntry.CapabilitiesChanged) ce).getCaps();
+ // Since the wifi network hasn't brought up,
+ // ConnectivityService#applyUnderlyingCapabilities cannot find it. Update
+ // NetworkCapabilities#mUnderlyingNetworks to an empty array, and it will be updated to
+ // the correct underlying networks once the wifi network brings up. But this case
+ // shouldn't happen in reality since no one could get the network which hasn't brought
+ // up. For the empty array of underlying networks, it should be happened for 2 cases,
+ // the first one is that the VPN app declares an empty array for its underlying
+ // networks, the second one is that the underlying networks are torn down.
+ //
+ // It shouldn't be null since the null value means the underlying networks of this
+ // network should follow the default network.
+ final ArrayList<Network> underlyingNetwork = new ArrayList<>();
+ assertEquals(underlyingNetwork, vpnNc1.getUnderlyingNetworks());
+ // Since the wifi network isn't exist, applyUnderlyingCapabilities()
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
- // Make that underlying network connect, and expect to see its capabilities immediately
- // reflected in the VPN's capabilities.
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
- mWiFiNetworkAgent.connect(false);
- // TODO: the callback for the VPN happens before any callbacks are called for the wifi
- // network that has just connected. There appear to be two issues here:
- // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for
- // it returns non-null (which happens very early, during handleRegisterNetworkAgent).
- // This is not correct because that that point the network is not connected and cannot
- // pass any traffic.
- // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
- // before rematching networks.
- // Given that this scenario can't really happen, this is probably fine for now.
- callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
- callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasTransport(TRANSPORT_VPN));
- assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasTransport(TRANSPORT_WIFI));
+ // Make that underlying network connect, and expect to see its capabilities immediately
+ // reflected in the VPN's capabilities.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
+ mWiFiNetworkAgent.connect(false);
+ // TODO: the callback for the VPN happens before any callbacks are called for the wifi
+ // network that has just connected. There appear to be two issues here:
+ // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities()
+ // for it returns non-null (which happens very early, during
+ // handleRegisterNetworkAgent).
+ // This is not correct because that that point the network is not connected and
+ // cannot pass any traffic.
+ // 2. When a network connects, updateNetworkInfo propagates underlying network
+ // capabilities before rematching networks.
+ // Given that this scenario can't really happen, this is probably fine for now.
+ ce = callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+ final NetworkCapabilities vpnNc2 = ((CallbackEntry.CapabilitiesChanged) ce).getCaps();
+ // The wifi network is brought up, NetworkCapabilities#mUnderlyingNetworks is updated to
+ // it.
+ underlyingNetwork.add(wifiNetwork);
+ assertEquals(underlyingNetwork, vpnNc2.getUnderlyingNetworks());
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
- // Disconnect the network, and expect to see the VPN capabilities change accordingly.
- mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- callback.expectCapabilitiesThat(mMockVpn, (nc) ->
- nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
+ // Disconnect the network, and expect to see the VPN capabilities change accordingly.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(mMockVpn, (nc) ->
+ nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
- mMockVpn.disconnect();
- mCm.unregisterNetworkCallback(callback);
+ mMockVpn.disconnect();
+ mCm.unregisterNetworkCallback(callback);
+ });
}
private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) {
@@ -7686,8 +8244,8 @@
callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps)
-> caps.hasCapability(NET_CAPABILITY_VALIDATED));
- when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
- .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
+ doReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)).when(mPackageManager)
+ .getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER);
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
@@ -7771,10 +8329,10 @@
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
// Start the restricted profile, and check that the UID within it loses network access.
- when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
- .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
- when(mUserManager.getAliveUsers()).thenReturn(asList(PRIMARY_USER_INFO,
- RESTRICTED_USER_INFO));
+ doReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)).when(mPackageManager)
+ .getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER);
+ doReturn(asList(PRIMARY_USER_INFO, RESTRICTED_USER_INFO)).when(mUserManager)
+ .getAliveUsers();
// TODO: check that VPN app within restricted profile still has access, etc.
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
@@ -7784,7 +8342,7 @@
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
// Stop the restricted profile, and check that the UID within it has network access again.
- when(mUserManager.getAliveUsers()).thenReturn(asList(PRIMARY_USER_INFO));
+ doReturn(asList(PRIMARY_USER_INFO)).when(mUserManager).getAliveUsers();
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
@@ -8485,7 +9043,7 @@
private void setupLegacyLockdownVpn() {
final String profileName = "testVpnProfile";
final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
- when(mVpnProfileStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag);
+ doReturn(profileTag).when(mVpnProfileStore).get(Credentials.LOCKDOWN_VPN);
final VpnProfile profile = new VpnProfile(profileName);
profile.name = "My VPN";
@@ -8493,7 +9051,7 @@
profile.dnsServers = "8.8.8.8";
profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
final byte[] encodedProfile = profile.encode();
- when(mVpnProfileStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
+ doReturn(encodedProfile).when(mVpnProfileStore).get(Credentials.VPN + profileName);
}
private void establishLegacyLockdownVpn(Network underlying) throws Exception {
@@ -8775,18 +9333,20 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+ final ArrayTrackRecord<ReportedInterfaces>.ReadHead readHead =
+ mDeps.mReportedInterfaceHistory.newReadHead();
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
cellLp.getInterfaceName(),
- new int[] { TRANSPORT_CELLULAR });
+ new int[] { TRANSPORT_CELLULAR })));
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName("wifi0");
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
mWiFiNetworkAgent.connect(true);
waitForIdle();
- verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
wifiLp.getInterfaceName(),
- new int[] { TRANSPORT_WIFI });
+ new int[] { TRANSPORT_WIFI })));
mCellNetworkAgent.disconnect();
mWiFiNetworkAgent.disconnect();
@@ -8795,9 +9355,9 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
cellLp.getInterfaceName(),
- new int[] { TRANSPORT_CELLULAR });
+ new int[] { TRANSPORT_CELLULAR })));
mCellNetworkAgent.disconnect();
}
@@ -8880,9 +9440,11 @@
assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
- verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+ final ArrayTrackRecord<ReportedInterfaces>.ReadHead readHead =
+ mDeps.mReportedInterfaceHistory.newReadHead();
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
cellLp.getInterfaceName(),
- new int[] { TRANSPORT_CELLULAR });
+ new int[] { TRANSPORT_CELLULAR })));
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -8901,15 +9463,15 @@
// Make sure BatteryStats was not told about any v4- interfaces, as none should have
// come online yet.
waitForIdle();
- verify(mDeps, never())
- .reportNetworkInterfaceForTransports(eq(mServiceContext), startsWith("v4-"), any());
+ assertNull(readHead.poll(0 /* timeout */, ri -> mServiceContext.equals(ri.context)
+ && ri.iface != null && ri.iface.startsWith("v4-")));
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
reset(mMockDnsResolver);
- when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
// Remove IPv4 address. Expect prefix discovery to be started again.
cellLp.removeLinkAddress(myIpv4);
@@ -8954,13 +9516,13 @@
assertTrue(CollectionUtils.contains(resolvrParams.servers, "8.8.8.8"));
for (final LinkProperties stackedLp : stackedLpsAfterChange) {
- verify(mDeps).reportNetworkInterfaceForTransports(
- mServiceContext, stackedLp.getInterfaceName(),
- new int[] { TRANSPORT_CELLULAR });
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
+ stackedLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR })));
}
reset(mMockNetd);
- when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
// Change the NAT64 prefix without first removing it.
// Expect clatd to be stopped and started with the new prefix.
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
@@ -9008,8 +9570,8 @@
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
reset(mMockDnsResolver);
- when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
// Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
@@ -9063,8 +9625,8 @@
// Test disconnecting a network that is running 464xlat.
// Connect a network with a NAT64 prefix.
- when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
cellLp.setNat64Prefix(kNat64Prefix);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(false /* validated */);
@@ -9273,7 +9835,7 @@
@Test
public void testWith464XlatDisable() throws Exception {
- doReturn(false).when(mDeps).getCellular464XlatEnabled();
+ mDeps.setCellular464XlatEnabled(false);
final TestNetworkCallback callback = new TestNetworkCallback();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
@@ -9433,7 +9995,7 @@
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
- when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo);
+ mProxyTracker.setGlobalProxy(testProxyInfo);
assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork));
}
@@ -9609,6 +10171,19 @@
}
@Test
+ public void testStartVpnProfileFromDiffPackage() throws Exception {
+ final String notMyVpnPkg = "com.not.my.vpn";
+ assertThrows(
+ SecurityException.class, () -> mVpnManagerService.startVpnProfile(notMyVpnPkg));
+ }
+
+ @Test
+ public void testStopVpnProfileFromDiffPackage() throws Exception {
+ final String notMyVpnPkg = "com.not.my.vpn";
+ assertThrows(SecurityException.class, () -> mVpnManagerService.stopVpnProfile(notMyVpnPkg));
+ }
+
+ @Test
public void testUidUpdateChangesInterfaceFilteringRule() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
@@ -9696,16 +10271,16 @@
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = targetSdk;
- when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
- .thenReturn(applicationInfo);
- when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk);
+ doReturn(applicationInfo).when(mPackageManager)
+ .getApplicationInfoAsUser(anyString(), anyInt(), any());
+ doReturn(targetSdk).when(mPackageManager).getTargetSdkVersion(any());
- when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+ doReturn(locationToggle).when(mLocationManager).isLocationEnabledForUser(any());
if (op != null) {
- when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()),
- eq(mContext.getPackageName()), eq(getAttributionTag()), anyString()))
- .thenReturn(AppOpsManager.MODE_ALLOWED);
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).noteOp(
+ eq(op), eq(Process.myUid()), eq(mContext.getPackageName()),
+ eq(getAttributionTag()), anyString());
}
if (perm != null) {
@@ -9727,7 +10302,7 @@
int callerUid, boolean includeLocationSensitiveInfo,
boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions()).thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION).when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9884,8 +10459,8 @@
mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_GRANTED);
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions())
- .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS)
+ .when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9903,8 +10478,8 @@
mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED);
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions())
- .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS)
+ .when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9923,8 +10498,8 @@
mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions())
- .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS)
+ .when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9942,8 +10517,8 @@
mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED);
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions())
- .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS)
+ .when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -10030,7 +10605,7 @@
@NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid,
@NonNull TransportInfo actualTransportInfo, int expectedOwnerUid,
@NonNull TransportInfo expectedTransportInfo) throws Exception {
- when(mPackageManager.getTargetSdkVersion(anyString())).thenReturn(Build.VERSION_CODES.S);
+ doReturn(Build.VERSION_CODES.S).when(mPackageManager).getTargetSdkVersion(anyString());
final NetworkCapabilities ncTemplate =
new NetworkCapabilities()
.addTransportType(TRANSPORT_WIFI)
@@ -10118,7 +10693,7 @@
final UnderlyingNetworkInfo underlyingNetworkInfo =
new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<>());
mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo);
- doReturn(42).when(mDeps).getConnectionOwnerUid(anyInt(), any(), any());
+ mDeps.setConnectionOwnerUid(42);
}
private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -10260,7 +10835,7 @@
public void testRegisterUnregisterConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest wifiRequest =
new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
- when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+ doReturn(mIBinder).when(mConnectivityDiagnosticsCallback).asBinder();
mService.registerConnectivityDiagnosticsCallback(
mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
@@ -10283,7 +10858,7 @@
public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest wifiRequest =
new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
- when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+ doReturn(mIBinder).when(mConnectivityDiagnosticsCallback).asBinder();
mService.registerConnectivityDiagnosticsCallback(
mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
@@ -10351,6 +10926,14 @@
return fakeNai(wifiNc, info);
}
+ private NetworkAgentInfo fakeVpnNai(NetworkCapabilities nc) {
+ final NetworkCapabilities vpnNc = new NetworkCapabilities.Builder(nc)
+ .addTransportType(TRANSPORT_VPN).build();
+ final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0 /* subtype */,
+ ConnectivityManager.getNetworkTypeName(TYPE_VPN), "" /* subtypeName */);
+ return fakeNai(vpnNc, info);
+ }
+
private NetworkAgentInfo fakeNai(NetworkCapabilities nc, NetworkInfo networkInfo) {
return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(),
nc, new NetworkScore.Builder().setLegacyInt(0).build(),
@@ -10485,6 +11068,36 @@
}
@Test
+ public void testUnderlyingNetworksWillBeSetInNetworkAgentInfoConstructor() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastT());
+ final Network network1 = new Network(100);
+ final Network network2 = new Network(101);
+ final List<Network> underlyingNetworks = new ArrayList<>();
+ final NetworkCapabilities ncWithEmptyUnderlyingNetworks = new NetworkCapabilities.Builder()
+ .setUnderlyingNetworks(underlyingNetworks)
+ .build();
+ final NetworkAgentInfo vpnNaiWithEmptyUnderlyingNetworks =
+ fakeVpnNai(ncWithEmptyUnderlyingNetworks);
+ assertEquals(underlyingNetworks,
+ Arrays.asList(vpnNaiWithEmptyUnderlyingNetworks.declaredUnderlyingNetworks));
+
+ underlyingNetworks.add(network1);
+ underlyingNetworks.add(network2);
+ final NetworkCapabilities ncWithUnderlyingNetworks = new NetworkCapabilities.Builder()
+ .setUnderlyingNetworks(underlyingNetworks)
+ .build();
+ final NetworkAgentInfo vpnNaiWithUnderlyingNetwokrs = fakeVpnNai(ncWithUnderlyingNetworks);
+ assertEquals(underlyingNetworks,
+ Arrays.asList(vpnNaiWithUnderlyingNetwokrs.declaredUnderlyingNetworks));
+
+ final NetworkCapabilities ncWithoutUnderlyingNetworks = new NetworkCapabilities.Builder()
+ .build();
+ final NetworkAgentInfo vpnNaiWithoutUnderlyingNetwokrs =
+ fakeVpnNai(ncWithoutUnderlyingNetworks);
+ assertNull(vpnNaiWithoutUnderlyingNetwokrs.declaredUnderlyingNetworks);
+ }
+
+ @Test
public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport()
throws Exception {
// Set up the Network, which leads to a ConnectivityReport being cached for the network.
@@ -10498,7 +11111,7 @@
callback.assertNoCallback();
final NetworkRequest request = new NetworkRequest.Builder().build();
- when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+ doReturn(mIBinder).when(mConnectivityDiagnosticsCallback).asBinder();
mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
@@ -10514,7 +11127,7 @@
private void setUpConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
- when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+ doReturn(mIBinder).when(mConnectivityDiagnosticsCallback).asBinder();
mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
@@ -10790,6 +11403,7 @@
assertNull(mService.getProxyForNetwork(null));
assertNull(mCm.getDefaultProxy());
+ final ExpectedBroadcast b1 = registerPacProxyBroadcast();
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
@@ -10799,9 +11413,10 @@
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
// VPN is connected but proxy is not set, so there is no need to send proxy broadcast.
- verify(mProxyTracker, never()).sendProxyBroadcast();
+ b1.expectNoBroadcast(500);
// Update to new range which is old range minus APP1, i.e. only APP2
+ final ExpectedBroadcast b2 = registerPacProxyBroadcast();
final Set<UidRange> newRanges = new HashSet<>(asList(
new UidRange(vpnRange.start, APP1_UID - 1),
new UidRange(APP1_UID + 1, vpnRange.stop)));
@@ -10812,37 +11427,37 @@
assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID);
// Uid has changed but proxy is not set, so there is no need to send proxy broadcast.
- verify(mProxyTracker, never()).sendProxyBroadcast();
+ b2.expectNoBroadcast(500);
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+ final ExpectedBroadcast b3 = registerPacProxyBroadcast();
lp.setHttpProxy(testProxyInfo);
mMockVpn.sendLinkProperties(lp);
waitForIdle();
// Proxy is set, so send a proxy broadcast.
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
- reset(mProxyTracker);
+ b3.expectBroadcast();
+ final ExpectedBroadcast b4 = registerPacProxyBroadcast();
mMockVpn.setUids(vpnRanges);
waitForIdle();
// Uid has changed and proxy is already set, so send a proxy broadcast.
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
- reset(mProxyTracker);
+ b4.expectBroadcast();
+ final ExpectedBroadcast b5 = registerPacProxyBroadcast();
// Proxy is removed, send a proxy broadcast.
lp.setHttpProxy(null);
mMockVpn.sendLinkProperties(lp);
waitForIdle();
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
- reset(mProxyTracker);
+ b5.expectBroadcast();
// Proxy is added in WiFi(default network), setDefaultProxy will be called.
final LinkProperties wifiLp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork());
assertNotNull(wifiLp);
+ final ExpectedBroadcast b6 = expectProxyChangeAction(testProxyInfo);
wifiLp.setHttpProxy(testProxyInfo);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
- verify(mProxyTracker, times(1)).setDefaultProxy(eq(testProxyInfo));
- reset(mProxyTracker);
+ b6.expectBroadcast();
}
@Test
@@ -10861,18 +11476,21 @@
lp.setHttpProxy(testProxyInfo);
final UidRange vpnRange = PRIMARY_UIDRANGE;
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
+ final ExpectedBroadcast b1 = registerPacProxyBroadcast();
mMockVpn.setOwnerAndAdminUid(VPN_UID);
mMockVpn.registerAgent(false, vpnRanges, lp);
// In any case, the proxy broadcast won't be sent before VPN goes into CONNECTED state.
// Otherwise, the app that calls ConnectivityManager#getDefaultProxy() when it receives the
// proxy broadcast will get null.
- verify(mProxyTracker, never()).sendProxyBroadcast();
+ b1.expectNoBroadcast(500);
+
+ final ExpectedBroadcast b2 = registerPacProxyBroadcast();
mMockVpn.connect(true /* validated */, true /* hasInternet */, false /* isStrictMode */);
waitForIdle();
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
// Vpn is connected with proxy, so the proxy broadcast will be sent to inform the apps to
// update their proxy data.
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
+ b2.expectBroadcast();
}
@Test
@@ -10901,10 +11519,10 @@
final LinkProperties cellularLp = new LinkProperties();
cellularLp.setInterfaceName(MOBILE_IFNAME);
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+ final ExpectedBroadcast b = registerPacProxyBroadcast();
cellularLp.setHttpProxy(testProxyInfo);
mCellNetworkAgent.sendLinkProperties(cellularLp);
- waitForIdle();
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
+ b.expectBroadcast();
}
@Test
@@ -11023,8 +11641,8 @@
final Pair<IQosCallback, IBinder> pair = createQosCallback();
mCallback = pair.first;
- when(mFilter.getNetwork()).thenReturn(network);
- when(mFilter.validate()).thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(network).when(mFilter).getNetwork();
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE).when(mFilter).validate();
mAgentWrapper = mCellNetworkAgent;
}
@@ -11046,8 +11664,8 @@
private Pair<IQosCallback, IBinder> createQosCallback() {
final IQosCallback callback = mock(IQosCallback.class);
final IBinder binder = mock(Binder.class);
- when(callback.asBinder()).thenReturn(binder);
- when(binder.isBinderAlive()).thenReturn(true);
+ doReturn(binder).when(callback).asBinder();
+ doReturn(true).when(binder).isBinderAlive();
return new Pair<>(callback, binder);
}
@@ -11057,8 +11675,8 @@
mQosCallbackMockHelper = new QosCallbackMockHelper();
final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper;
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
@@ -11081,8 +11699,8 @@
public void testQosCallbackNoRegistrationOnValidationError() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED)
+ .when(mQosCallbackMockHelper.mFilter).validate();
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
waitForIdle();
@@ -11096,8 +11714,8 @@
final int sessionId = 10;
final int qosCallbackId = 1;
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
waitForIdle();
@@ -11126,8 +11744,8 @@
final int sessionId = 10;
final int qosCallbackId = 1;
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
waitForIdle();
@@ -11154,8 +11772,8 @@
public void testQosCallbackTooManyRequests() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
for (int i = 0; i < 100; i++) {
final Pair<IQosCallback, IBinder> pair = createQosCallback();
@@ -11186,8 +11804,8 @@
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = uid;
try {
- when(mPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), eq(user)))
- .thenReturn(applicationInfo);
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfoAsUser(
+ eq(packageName), anyInt(), eq(user));
} catch (Exception e) {
fail(e.getMessage());
}
@@ -11196,13 +11814,12 @@
private void mockGetApplicationInfoThrowsNameNotFound(@NonNull final String packageName,
@NonNull final UserHandle user)
throws Exception {
- when(mPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), eq(user)))
- .thenThrow(new PackageManager.NameNotFoundException(packageName));
+ doThrow(new PackageManager.NameNotFoundException(packageName)).when(
+ mPackageManager).getApplicationInfoAsUser(eq(packageName), anyInt(), eq(user));
}
private void mockHasSystemFeature(@NonNull final String featureName, final boolean hasFeature) {
- when(mPackageManager.hasSystemFeature(eq(featureName)))
- .thenReturn(hasFeature);
+ doReturn(hasFeature).when(mPackageManager).hasSystemFeature(eq(featureName));
}
private Range<Integer> getNriFirstUidRange(@NonNull final NetworkRequestInfo nri) {
@@ -11402,8 +12019,8 @@
// Arrange users
final int secondUserTestPackageUid = UserHandle.getUid(SECONDARY_USER, TEST_PACKAGE_UID);
final int thirdUserTestPackageUid = UserHandle.getUid(TERTIARY_USER, TEST_PACKAGE_UID);
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE, SECONDARY_USER_HANDLE, TERTIARY_USER_HANDLE));
+ doReturn(asList(PRIMARY_USER_HANDLE, SECONDARY_USER_HANDLE, TERTIARY_USER_HANDLE))
+ .when(mUserManager).getUserHandles(anyBoolean());
// Arrange PackageManager mocks testing for users who have and don't have a package.
mockGetApplicationInfoThrowsNameNotFound(TEST_PACKAGE_NAME, PRIMARY_USER_HANDLE);
@@ -11638,6 +12255,8 @@
private void registerDefaultNetworkCallbacks() {
if (mSystemDefaultNetworkCallback != null || mDefaultNetworkCallback != null
|| mProfileDefaultNetworkCallback != null
+ || mProfileDefaultNetworkCallbackAsAppUid2 != null
+ || mTestPackageDefaultNetworkCallback2 != null
|| mTestPackageDefaultNetworkCallback != null) {
throw new IllegalStateException("Default network callbacks already registered");
}
@@ -11648,12 +12267,18 @@
mDefaultNetworkCallback = new TestNetworkCallback();
mProfileDefaultNetworkCallback = new TestNetworkCallback();
mTestPackageDefaultNetworkCallback = new TestNetworkCallback();
+ mProfileDefaultNetworkCallbackAsAppUid2 = new TestNetworkCallback();
+ mTestPackageDefaultNetworkCallback2 = new TestNetworkCallback();
mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
new Handler(ConnectivityThread.getInstanceLooper()));
mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback,
TEST_WORK_PROFILE_APP_UID);
registerDefaultNetworkCallbackAsUid(mTestPackageDefaultNetworkCallback, TEST_PACKAGE_UID);
+ registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallbackAsAppUid2,
+ TEST_WORK_PROFILE_APP_UID_2);
+ registerDefaultNetworkCallbackAsUid(mTestPackageDefaultNetworkCallback2,
+ TEST_PACKAGE_UID2);
// TODO: test using ConnectivityManager#registerDefaultNetworkCallbackAsUid as well.
mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED);
}
@@ -11671,6 +12296,12 @@
if (null != mTestPackageDefaultNetworkCallback) {
mCm.unregisterNetworkCallback(mTestPackageDefaultNetworkCallback);
}
+ if (null != mProfileDefaultNetworkCallbackAsAppUid2) {
+ mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallbackAsAppUid2);
+ }
+ if (null != mTestPackageDefaultNetworkCallback2) {
+ mCm.unregisterNetworkCallback(mTestPackageDefaultNetworkCallback2);
+ }
}
private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -12349,8 +12980,8 @@
// Arrange users
final int secondUser = 10;
final UserHandle secondUserHandle = new UserHandle(secondUser);
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE, secondUserHandle));
+ doReturn(asList(PRIMARY_USER_HANDLE, secondUserHandle)).when(mUserManager)
+ .getUserHandles(anyBoolean());
// Arrange PackageManager mocks
final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
@@ -12390,8 +13021,7 @@
// Arrange users
final int secondUser = 10;
final UserHandle secondUserHandle = new UserHandle(secondUser);
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE));
+ doReturn(asList(PRIMARY_USER_HANDLE)).when(mUserManager).getUserHandles(anyBoolean());
// Arrange PackageManager mocks
final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
@@ -12418,8 +13048,8 @@
false /* shouldDestroyNetwork */);
// Send a broadcast indicating a user was added.
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE, secondUserHandle));
+ doReturn(asList(PRIMARY_USER_HANDLE, secondUserHandle)).when(mUserManager)
+ .getUserHandles(anyBoolean());
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser));
processBroadcast(addedIntent);
@@ -12431,8 +13061,7 @@
false /* shouldDestroyNetwork */);
// Send a broadcast indicating a user was removed.
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE));
+ doReturn(asList(PRIMARY_USER_HANDLE)).when(mUserManager).getUserHandles(anyBoolean());
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser));
processBroadcast(removedIntent);
@@ -12971,21 +13600,26 @@
assertLength(2, snapshots);
assertContainsAll(snapshots, cellSnapshot, wifiSnapshot);
- // Set cellular as suspended, verify the snapshots will not contain suspended networks.
- // TODO: Consider include SUSPENDED networks, which should be considered as
- // temporary shortage of connectivity of a connected network.
+ // Set cellular as suspended, verify the snapshots will contain suspended networks.
mCellNetworkAgent.suspend();
waitForIdle();
+ final NetworkCapabilities cellSuspendedNc =
+ mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork());
+ assertFalse(cellSuspendedNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ final NetworkStateSnapshot cellSuspendedSnapshot = new NetworkStateSnapshot(
+ mCellNetworkAgent.getNetwork(), cellSuspendedNc, cellLp,
+ null, ConnectivityManager.TYPE_MOBILE);
snapshots = mCm.getAllNetworkStateSnapshots();
- assertLength(1, snapshots);
- assertEquals(wifiSnapshot, snapshots.get(0));
+ assertLength(2, snapshots);
+ assertContainsAll(snapshots, cellSuspendedSnapshot, wifiSnapshot);
- // Disconnect wifi, verify the snapshots contain nothing.
+ // Disconnect wifi, verify the snapshots contain only cellular.
mWiFiNetworkAgent.disconnect();
waitForIdle();
snapshots = mCm.getAllNetworkStateSnapshots();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- assertLength(0, snapshots);
+ assertLength(1, snapshots);
+ assertEquals(cellSuspendedSnapshot, snapshots.get(0));
mCellNetworkAgent.resume();
waitForIdle();
@@ -13096,10 +13730,34 @@
}
private UidRangeParcel[] uidRangeFor(final UserHandle handle) {
- UidRange range = UidRange.createForUser(handle);
+ final UidRange range = UidRange.createForUser(handle);
return new UidRangeParcel[] { new UidRangeParcel(range.start, range.stop) };
}
+ private UidRangeParcel[] uidRangeFor(final UserHandle handle,
+ ProfileNetworkPreference profileNetworkPreference) {
+ final Set<UidRange> uidRangeSet;
+ UidRange range = UidRange.createForUser(handle);
+ if (profileNetworkPreference.getIncludedUids().size() != 0) {
+ uidRangeSet = UidRangeUtils.convertListToUidRange(
+ profileNetworkPreference.getIncludedUids());
+ } else if (profileNetworkPreference.getExcludedUids().size() != 0) {
+ uidRangeSet = UidRangeUtils.removeRangeSetFromUidRange(
+ range, UidRangeUtils.convertListToUidRange(
+ profileNetworkPreference.getExcludedUids()));
+ } else {
+ uidRangeSet = new ArraySet<>();
+ uidRangeSet.add(range);
+ }
+ UidRangeParcel[] uidRangeParcels = new UidRangeParcel[uidRangeSet.size()];
+ int i = 0;
+ for (UidRange range1 : uidRangeSet) {
+ uidRangeParcels[i] = new UidRangeParcel(range1.start, range1.stop);
+ i++;
+ }
+ return uidRangeParcels;
+ }
+
private static class TestOnCompleteListener implements Runnable {
final class OnComplete {}
final ArrayTrackRecord<OnComplete>.ReadHead mHistory =
@@ -13145,72 +13803,112 @@
}
/**
- * Make sure per-profile networking preference behaves as expected when the enterprise network
- * goes up and down while the preference is active. Make sure they behave as expected whether
- * there is a general default network or not.
+ * Make sure per profile network preferences behave as expected for a given
+ * profile network preference.
*/
- @Test
- public void testPreferenceForUserNetworkUpDown() throws Exception {
+ public void testPreferenceForUserNetworkUpDownForGivenPreference(
+ ProfileNetworkPreference profileNetworkPreference,
+ boolean connectWorkProfileAgentAhead,
+ UserHandle testHandle,
+ TestNetworkCallback profileDefaultNetworkCallback,
+ TestNetworkCallback disAllowProfileDefaultNetworkCallback) throws Exception {
final InOrder inOrder = inOrder(mMockNetd);
- final UserHandle testHandle = setupEnterpriseNetwork();
- registerDefaultNetworkCallbacks();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ profileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ disAllowProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(
+ mCellNetworkAgent);
+ }
inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
+ final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
+ if (connectWorkProfileAgentAhead) {
+ workAgent.connect(false);
+ }
final TestOnCompleteListener listener = new TestOnCompleteListener();
- mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ mCm.setProfileNetworkPreferences(testHandle, List.of(profileNetworkPreference),
r -> r.run(), listener);
listener.expectOnComplete();
-
- // Setting a network preference for this user will create a new set of routing rules for
- // the UID range that corresponds to this user, so as to define the default network
- // for these apps separately. This is true because the multi-layer request relevant to
- // this UID range contains a TRACK_DEFAULT, so the range will be moved through UID-specific
- // rules to the correct network – in this case the system default network. The case where
- // the default network for the profile happens to be the same as the system default
- // is not handled specially, the rules are always active as long as a preference is set.
- inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
- mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle),
- PREFERENCE_ORDER_PROFILE));
+ boolean allowFallback = true;
+ if (profileNetworkPreference.getPreference()
+ == PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK) {
+ allowFallback = false;
+ }
+ if (allowFallback) {
+ // Setting a network preference for this user will create a new set of routing rules for
+ // the UID range that corresponds to this user, inorder to define the default network
+ // for these apps separately. This is true because the multi-layer request relevant to
+ // this UID range contains a TRACK_DEFAULT, so the range will be moved through
+ // UID-specific rules to the correct network – in this case the system default network.
+ // The case where the default network for the profile happens to be the same as the
+ // system default is not handled specially, the rules are always active as long as
+ // a preference is set.
+ inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreference),
+ PREFERENCE_ORDER_PROFILE));
+ }
// The enterprise network is not ready yet.
- assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
- mProfileDefaultNetworkCallback);
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ if (allowFallback) {
+ assertNoCallbacks(profileDefaultNetworkCallback);
+ } else if (!connectWorkProfileAgentAhead) {
+ profileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
+ }
+ }
- final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
- workAgent.connect(false);
+ if (!connectWorkProfileAgentAhead) {
+ workAgent.connect(false);
+ }
- mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
+ profileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ disAllowProfileDefaultNetworkCallback.assertNoCallback();
+ }
mSystemDefaultNetworkCallback.assertNoCallback();
mDefaultNetworkCallback.assertNoCallback();
inOrder.verify(mMockNetd).networkCreate(
nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM));
inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
- workAgent.getNetwork().netId, uidRangeFor(testHandle), PREFERENCE_ORDER_PROFILE));
- inOrder.verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
- mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle),
+ workAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreference),
PREFERENCE_ORDER_PROFILE));
+ if (allowFallback) {
+ inOrder.verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+ mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreference),
+ PREFERENCE_ORDER_PROFILE));
+ }
+
// Make sure changes to the work agent send callbacks to the app in the work profile, but
// not to the other apps.
workAgent.setNetworkValid(true /* isStrictMode */);
workAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
- mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent,
+ profileDefaultNetworkCallback.expectCapabilitiesThat(workAgent,
nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)
&& nc.hasCapability(NET_CAPABILITY_ENTERPRISE));
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
+ }
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
workAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
- mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc ->
+ profileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc ->
nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
+ }
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
// Conversely, change a capability on the system-wide default network and make sure
@@ -13220,7 +13918,11 @@
nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
- mProfileDefaultNetworkCallback.assertNoCallback();
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ disAllowProfileDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
+ nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ }
+ profileDefaultNetworkCallback.assertNoCallback();
// Disconnect and reconnect the system-wide default network and make sure that the
// apps on this network see the appropriate callbacks, and the app on the work profile
@@ -13228,32 +13930,55 @@
mCellNetworkAgent.disconnect();
mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- mProfileDefaultNetworkCallback.assertNoCallback();
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ disAllowProfileDefaultNetworkCallback.expectCallback(
+ CallbackEntry.LOST, mCellNetworkAgent);
+ }
+ profileDefaultNetworkCallback.assertNoCallback();
inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- mProfileDefaultNetworkCallback.assertNoCallback();
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ disAllowProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(
+ mCellNetworkAgent);
+
+ }
+ profileDefaultNetworkCallback.assertNoCallback();
inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
// When the agent disconnects, test that the app on the work profile falls back to the
// default network.
workAgent.disconnect();
- mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent);
- mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ profileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent);
+ if (allowFallback) {
+ profileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
+ }
+ }
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
- inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
- mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle),
- PREFERENCE_ORDER_PROFILE));
+ if (allowFallback) {
+ inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreference),
+ PREFERENCE_ORDER_PROFILE));
+ }
inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId);
mCellNetworkAgent.disconnect();
mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ disAllowProfileDefaultNetworkCallback.expectCallback(
+ CallbackEntry.LOST, mCellNetworkAgent);
+ }
+ if (allowFallback) {
+ profileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ }
// Waiting for the handler to be idle before checking for networkDestroy is necessary
// here because ConnectivityService calls onLost before the network is fully torn down.
@@ -13266,35 +13991,228 @@
final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent();
workAgent2.connect(false);
- mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
+ profileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
+ }
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM));
inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
- workAgent2.getNetwork().netId, uidRangeFor(testHandle), PREFERENCE_ORDER_PROFILE));
+ workAgent2.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreference), PREFERENCE_ORDER_PROFILE));
workAgent2.setNetworkValid(true /* isStrictMode */);
workAgent2.mNetworkMonitor.forceReevaluation(Process.myUid());
- mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2,
+ profileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2,
nc -> nc.hasCapability(NET_CAPABILITY_ENTERPRISE)
&& !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
+ }
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
inOrder.verify(mMockNetd, never()).networkAddUidRangesParcel(any());
- // When the agent disconnects, test that the app on the work profile falls back to the
+ // When the agent disconnects, test that the app on the work profile fall back to the
// default network.
workAgent2.disconnect();
- mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2);
+ profileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2);
+ if (disAllowProfileDefaultNetworkCallback != null) {
+ assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
+ }
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
- mProfileDefaultNetworkCallback);
+ profileDefaultNetworkCallback);
// Callbacks will be unregistered by tearDown()
}
/**
+ * Make sure per-profile networking preference behaves as expected when the enterprise network
+ * goes up and down while the preference is active. Make sure they behave as expected whether
+ * there is a general default network or not.
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDown() throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), false,
+ testHandle, mProfileDefaultNetworkCallback, null);
+ }
+
+ /**
+ * Make sure per-profile networking preference behaves as expected when the enterprise network
+ * goes up and down while the preference is active. Make sure they behave as expected whether
+ * there is a general default network or not when configured to not fallback to default network.
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDownWithNoFallback() throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), false,
+ testHandle, mProfileDefaultNetworkCallback, null);
+ }
+
+ /**
+ * Make sure per-profile networking preference behaves as expected when the enterprise network
+ * goes up and down while the preference is active. Make sure they behave as expected whether
+ * there is a general default network or not when configured to not fallback to default network
+ * along with already connected enterprise work agent
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDownWithNoFallbackWithAlreadyConnectedWorkAgent()
+ throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), true, testHandle,
+ mProfileDefaultNetworkCallback, null);
+ }
+
+ /**
+ * Make sure per-profile networking preference for specific uid of test handle
+ * behaves as expected
+ */
+ @Test
+ public void testPreferenceForDefaultUidOfTestHandle() throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setIncludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID)));
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), false, testHandle,
+ mProfileDefaultNetworkCallback, null);
+ }
+
+ /**
+ * Make sure per-profile networking preference for specific uid of test handle
+ * behaves as expected
+ */
+ @Test
+ public void testPreferenceForSpecificUidOfOnlyOneApp() throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setIncludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), false,
+ testHandle, mProfileDefaultNetworkCallbackAsAppUid2, null);
+ }
+
+ /**
+ * Make sure per-profile networking preference for specific uid of test handle
+ * behaves as expected
+ */
+ @Test
+ public void testPreferenceForDisallowSpecificUidOfApp() throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setExcludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), false,
+ testHandle, mProfileDefaultNetworkCallback,
+ mProfileDefaultNetworkCallbackAsAppUid2);
+ }
+
+ /**
+ * Make sure per-profile networking preference for specific uid of test handle
+ * invalid uid inputs
+ */
+ @Test
+ public void testPreferenceForInvalidUids() throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setExcludedUids(
+ List.of(testHandle.getUid(0) - 1));
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ Assert.assertThrows(IllegalArgumentException.class, () -> mCm.setProfileNetworkPreferences(
+ testHandle, List.of(profileNetworkPreferenceBuilder.build()),
+ r -> r.run(), listener));
+
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setIncludedUids(
+ List.of(testHandle.getUid(0) - 1));
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreferences(
+ testHandle, List.of(profileNetworkPreferenceBuilder.build()),
+ r -> r.run(), listener));
+
+
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setIncludedUids(
+ List.of(testHandle.getUid(0) - 1));
+ profileNetworkPreferenceBuilder.setExcludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreferences(
+ testHandle, List.of(profileNetworkPreferenceBuilder.build()),
+ r -> r.run(), listener));
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder2 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder2.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder2.setIncludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ profileNetworkPreferenceBuilder.setIncludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreferences(
+ testHandle, List.of(profileNetworkPreferenceBuilder.build(),
+ profileNetworkPreferenceBuilder2.build()),
+ r -> r.run(), listener));
+
+ profileNetworkPreferenceBuilder2.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder2.setExcludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ profileNetworkPreferenceBuilder.setExcludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreferences(
+ testHandle, List.of(profileNetworkPreferenceBuilder.build(),
+ profileNetworkPreferenceBuilder2.build()),
+ r -> r.run(), listener));
+
+ profileNetworkPreferenceBuilder2.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ profileNetworkPreferenceBuilder2.setExcludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ profileNetworkPreferenceBuilder.setExcludedUids(
+ List.of(testHandle.getUid(TEST_WORK_PROFILE_APP_UID_2)));
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreferences(
+ testHandle, List.of(profileNetworkPreferenceBuilder.build(),
+ profileNetworkPreferenceBuilder2.build()),
+ r -> r.run(), listener));
+ }
+
+ /**
* Test that, in a given networking context, calling setPreferenceForUser to set per-profile
* defaults on then off works as expected.
*/
@@ -13447,7 +14365,8 @@
assertThrows("Should not be able to set an illegal preference",
IllegalArgumentException.class,
() -> mCm.setProfileNetworkPreference(testHandle,
- PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null));
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK + 1,
+ null, null));
}
/**
@@ -14019,7 +14938,7 @@
// callback.
final int[] uids2 = new int[] { TEST_WORK_PROFILE_APP_UID };
final UidRangeParcel[] uidRanges2 = toUidRangeStableParcels(uidRangesForUids(uids2));
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(Arrays.asList(testHandle));
+ doReturn(Arrays.asList(testHandle)).when(mUserManager).getUserHandles(anyBoolean());
setupSetOemNetworkPreferenceForPreferenceTest(
networkPref, uidRanges2, "com.android.test", testHandle);
mDefaultNetworkCallback.assertNoCallback();
@@ -14057,4 +14976,11 @@
mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
}
+
+ @Test
+ public void testRequestRouteToHostAddress_PackageDoesNotBelongToCaller() {
+ assertThrows(SecurityException.class, () -> mService.requestRouteToHostAddress(
+ ConnectivityManager.TYPE_NONE, null /* hostAddress */, "com.not.package.owner",
+ null /* callingAttributionTag */));
+ }
}
diff --git a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
index 5bbbe40..45f3d3c 100644
--- a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -60,6 +60,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.system.Os;
import android.test.mock.MockContext;
import android.util.ArraySet;
@@ -188,9 +189,15 @@
}
}
+ private IpSecService.Dependencies makeDependencies() throws RemoteException {
+ final IpSecService.Dependencies deps = mock(IpSecService.Dependencies.class);
+ when(deps.getNetdInstance(mTestContext)).thenReturn(mMockNetd);
+ return deps;
+ }
+
INetd mMockNetd;
PackageManager mMockPkgMgr;
- IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+ IpSecService.Dependencies mDeps;
IpSecService mIpSecService;
Network fakeNetwork = new Network(0xAB);
int mUid = Os.getuid();
@@ -219,11 +226,8 @@
public void setUp() throws Exception {
mMockNetd = mock(INetd.class);
mMockPkgMgr = mock(PackageManager.class);
- mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mTestContext, mMockIpSecSrvConfig);
-
- // Injecting mock netd
- when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+ mDeps = makeDependencies();
+ mIpSecService = new IpSecService(mTestContext, mDeps);
// PackageManager should always return true (feature flag tests in IpSecServiceTest)
when(mMockPkgMgr.hasSystemFeature(anyString())).thenReturn(true);
diff --git a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
index 6957d51..5c7ca6f 100644
--- a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
@@ -57,14 +57,14 @@
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpSecServiceRefcountedResourceTest {
Context mMockContext;
- IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+ IpSecService.Dependencies mMockDeps;
IpSecService mIpSecService;
@Before
public void setUp() throws Exception {
mMockContext = mock(Context.class);
- mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+ mMockDeps = mock(IpSecService.Dependencies.class);
+ mIpSecService = new IpSecService(mMockContext, mMockDeps);
}
private void assertResourceState(
diff --git a/tests/unit/java/com/android/server/IpSecServiceTest.java b/tests/unit/java/com/android/server/IpSecServiceTest.java
index fabd6f1..7e6b157 100644
--- a/tests/unit/java/com/android/server/IpSecServiceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceTest.java
@@ -46,6 +46,7 @@
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteException;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
@@ -122,24 +123,22 @@
Context mMockContext;
INetd mMockNetd;
- IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+ IpSecService.Dependencies mDeps;
IpSecService mIpSecService;
@Before
public void setUp() throws Exception {
mMockContext = mock(Context.class);
mMockNetd = mock(INetd.class);
- mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
-
- // Injecting mock netd
- when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+ mDeps = makeDependencies();
+ mIpSecService = new IpSecService(mMockContext, mDeps);
+ assertNotNull(mIpSecService);
}
- @Test
- public void testIpSecServiceCreate() throws InterruptedException {
- IpSecService ipSecSrv = IpSecService.create(mMockContext);
- assertNotNull(ipSecSrv);
+ private IpSecService.Dependencies makeDependencies() throws RemoteException {
+ final IpSecService.Dependencies deps = mock(IpSecService.Dependencies.class);
+ when(deps.getNetdInstance(mMockContext)).thenReturn(mMockNetd);
+ return deps;
}
@Test
@@ -611,7 +610,7 @@
public void testOpenUdpEncapSocketTagsSocket() throws Exception {
IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
IpSecService testIpSecService = new IpSecService(
- mMockContext, mMockIpSecSrvConfig, mockTagger);
+ mMockContext, mDeps, mockTagger);
IpSecUdpEncapResponse udpEncapResp =
testIpSecService.openUdpEncapsulationSocket(0, new Binder());
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index e80a938..6d1d765 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -16,7 +16,11 @@
package com.android.server;
+import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -27,16 +31,22 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.ContentResolver;
import android.content.Context;
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.INsdServiceConnector;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.server.NsdService.DaemonConnection;
@@ -48,13 +58,19 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import java.util.LinkedList;
+import java.util.Queue;
+
// TODOs:
// - test client can send requests and receive replies
// - test NSD_ON ENABLE/DISABLED listening
@@ -67,14 +83,30 @@
private static final long CLEANUP_DELAY_MS = 500;
private static final long TIMEOUT_MS = 500;
+ // Records INsdManagerCallback created when NsdService#connect is called.
+ // Only accessed on the test thread, since NsdService#connect is called by the NsdManager
+ // constructor called on the test thread.
+ private final Queue<INsdManagerCallback> mCreatedCallbacks = new LinkedList<>();
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
@Mock Context mContext;
@Mock ContentResolver mResolver;
- @Mock NsdService.NsdSettings mSettings;
NativeCallbackReceiver mDaemonCallback;
@Spy DaemonConnection mDaemon = new DaemonConnection(mDaemonCallback);
HandlerThread mThread;
TestHandler mHandler;
+ private static class LinkToDeathRecorder extends Binder {
+ IBinder.DeathRecipient mDr;
+
+ @Override
+ public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ super.linkToDeath(recipient, flags);
+ mDr = recipient;
+ }
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -94,43 +126,80 @@
}
@Test
- public void testNoDaemonStartedWhenClientsConnect() {
- when(mSettings.isEnabled()).thenReturn(true);
-
+ @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testPreSClients() throws Exception {
NsdService service = makeService();
- // Creating an NsdManager will not cause any cmds executed, which means
- // no daemon is started.
- NsdManager client1 = connectClient(service);
+ // Pre S client connected, the daemon should be started.
+ connectClient(service);
waitForIdle();
- verify(mDaemon, never()).execute(any());
+ final INsdManagerCallback cb1 = getCallback();
+ final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
+ verify(mDaemon, times(1)).maybeStart();
+ verifyDaemonCommands("start-service");
- // Creating another NsdManager will not cause any cmds executed.
- NsdManager client2 = connectClient(service);
+ connectClient(service);
waitForIdle();
- verify(mDaemon, never()).execute(any());
+ final INsdManagerCallback cb2 = getCallback();
+ final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
+ verify(mDaemon, times(1)).maybeStart();
- client1.disconnect();
+ deathRecipient1.binderDied();
// Still 1 client remains, daemon shouldn't be stopped.
waitForIdle();
verify(mDaemon, never()).maybeStop();
- client2.disconnect();
- // All clients are disconnected, stop the daemon after CLEANUP_DELAY_MS.
+ deathRecipient2.binderDied();
+ // All clients are disconnected, the daemon should be stopped.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
-
- client1.disconnect();
- client2.disconnect();
+ verifyDaemonCommands("stop-service");
}
@Test
- public void testClientRequestsAreGCedAtDisconnection() {
- when(mSettings.isEnabled()).thenReturn(true);
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testNoDaemonStartedWhenClientsConnect() throws Exception {
+ final NsdService service = makeService();
- NsdService service = makeService();
- NsdManager client = connectClient(service);
-
+ // Creating an NsdManager will not cause any cmds executed, which means
+ // no daemon is started.
+ connectClient(service);
waitForIdle();
+ verify(mDaemon, never()).execute(any());
+ final INsdManagerCallback cb1 = getCallback();
+ final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
+
+ // Creating another NsdManager will not cause any cmds executed.
+ connectClient(service);
+ waitForIdle();
+ verify(mDaemon, never()).execute(any());
+ final INsdManagerCallback cb2 = getCallback();
+ final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
+
+ // If there is no active request, try to clean up the daemon
+ // every time the client disconnects.
+ deathRecipient1.binderDied();
+ verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ reset(mDaemon);
+ deathRecipient2.binderDied();
+ verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ }
+
+ private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb)
+ throws Exception {
+ final IBinder.DeathRecipient dr = ((LinkToDeathRecorder) cb.asBinder()).mDr;
+ assertNotNull(dr);
+ return dr;
+ }
+
+ @Test
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testClientRequestsAreGCedAtDisconnection() throws Exception {
+ NsdService service = makeService();
+
+ NsdManager client = connectClient(service);
+ waitForIdle();
+ final INsdManagerCallback cb1 = getCallback();
+ final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
verify(mDaemon, never()).maybeStart();
verify(mDaemon, never()).execute(any());
@@ -159,19 +228,16 @@
verifyDaemonCommand("resolve 4 a_name a_type local.");
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
- client.disconnect();
+ deathRecipient.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
// checks that request are cleaned
verifyDaemonCommands("stop-register 2", "stop-discover 3",
"stop-resolve 4", "stop-service");
-
- client.disconnect();
}
@Test
- public void testCleanupDelayNoRequestActive() {
- when(mSettings.isEnabled()).thenReturn(true);
-
+ @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
+ public void testCleanupDelayNoRequestActive() throws Exception {
NsdService service = makeService();
NsdManager client = connectClient(service);
@@ -181,6 +247,8 @@
client.registerService(request, PROTOCOL, listener1);
waitForIdle();
verify(mDaemon, times(1)).maybeStart();
+ final INsdManagerCallback cb1 = getCallback();
+ final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
client.unregisterService(listener1);
@@ -189,7 +257,7 @@
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
verifyDaemonCommand("stop-service");
reset(mDaemon);
- client.disconnect();
+ deathRecipient.binderDied();
// Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
}
@@ -203,12 +271,28 @@
mDaemonCallback = callback;
return mDaemon;
};
- NsdService service = new NsdService(mContext, mSettings,
- mHandler, supplier, CLEANUP_DELAY_MS);
+ final NsdService service = new NsdService(mContext, mHandler, supplier, CLEANUP_DELAY_MS) {
+ @Override
+ public INsdServiceConnector connect(INsdManagerCallback baseCb) {
+ // Wrap the callback in a transparent mock, to mock asBinder returning a
+ // LinkToDeathRecorder. This will allow recording the binder death recipient
+ // registered on the callback. Use a transparent mock and not a spy as the actual
+ // implementation class is not public and cannot be spied on by Mockito.
+ final INsdManagerCallback cb = mock(INsdManagerCallback.class,
+ AdditionalAnswers.delegatesTo(baseCb));
+ doReturn(new LinkToDeathRecorder()).when(cb).asBinder();
+ mCreatedCallbacks.add(cb);
+ return super.connect(cb);
+ }
+ };
verify(mDaemon, never()).execute(any(String.class));
return service;
}
+ private INsdManagerCallback getCallback() {
+ return mCreatedCallbacks.remove();
+ }
+
NsdManager connectClient(NsdService service) {
return new NsdManager(mContext, service);
}
diff --git a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
index e2ad00d..c86e699 100644
--- a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -35,10 +35,12 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -60,6 +62,7 @@
import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
import android.util.DataUnit;
+import android.util.Range;
import android.util.RecurrenceRule;
import androidx.test.filters.SmallTest;
@@ -68,7 +71,6 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsManagerInternal;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -87,6 +89,7 @@
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
+import java.util.Set;
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
@@ -94,6 +97,7 @@
public class MultipathPolicyTrackerTest {
private static final Network TEST_NETWORK = new Network(123);
private static final int POLICY_SNOOZED = -100;
+ private static final String TEST_IMSI1 = "TEST_IMSI1";
@Mock private Context mContext;
@Mock private Context mUserAllContext;
@@ -105,7 +109,6 @@
@Mock private NetworkPolicyManager mNPM;
@Mock private NetworkStatsManager mStatsManager;
@Mock private NetworkPolicyManagerInternal mNPMI;
- @Mock private NetworkStatsManagerInternal mNetworkStatsManagerInternal;
@Mock private TelephonyManager mTelephonyManager;
private MockContentResolver mContentResolver;
@@ -148,6 +151,7 @@
when(mDeps.getClock()).thenReturn(mClock);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getSubscriberId()).thenReturn(TEST_IMSI1);
mContentResolver = Mockito.spy(new MockContentResolver(mContext));
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -162,9 +166,6 @@
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(NetworkPolicyManagerInternal.class, mNPMI);
- LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
- LocalServices.addService(NetworkStatsManagerInternal.class, mNetworkStatsManagerInternal);
-
mTracker = new MultipathPolicyTracker(mContext, mHandler, mDeps);
}
@@ -199,6 +200,11 @@
when(mNPMI.getSubscriptionOpportunisticQuota(TEST_NETWORK, QUOTA_TYPE_MULTIPATH))
.thenReturn(subscriptionQuota);
+ // Prepare stats to be mocked.
+ final NetworkStats.Bucket mockedStatsBucket = mock(NetworkStats.Bucket.class);
+ when(mockedStatsBucket.getTxBytes()).thenReturn(usedBytesToday / 3);
+ when(mockedStatsBucket.getRxBytes()).thenReturn(usedBytesToday - usedBytesToday / 3);
+
// Setup user policy warning / limit
if (policyWarning != WARNING_DISABLED || policyLimit != LIMIT_DISABLED) {
final Instant recurrenceStart = Instant.parse("2017-04-01T00:00:00Z");
@@ -212,7 +218,9 @@
final boolean snoozeLimit = policyLimit == POLICY_SNOOZED;
when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[] {
new NetworkPolicy(
- NetworkTemplate.buildTemplateMobileWildcard(),
+ new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+ .setSubscriberIds(Set.of(TEST_IMSI1))
+ .setMeteredness(android.net.NetworkStats.METERED_YES).build(),
recurrenceRule,
snoozeWarning ? 0 : policyWarning,
snoozeLimit ? 0 : policyLimit,
@@ -222,6 +230,13 @@
true /* metered */,
false /* inferred */)
});
+
+ // Mock stats for this month.
+ final Range<ZonedDateTime> cycleOfTheMonth = recurrenceRule.cycleIterator().next();
+ when(mStatsManager.querySummaryForDevice(any(),
+ eq(cycleOfTheMonth.getLower().toInstant().toEpochMilli()),
+ eq(cycleOfTheMonth.getUpper().toInstant().toEpochMilli())))
+ .thenReturn(mockedStatsBucket);
} else {
when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
}
@@ -233,10 +248,10 @@
when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes))
.thenReturn((int) defaultResSetting);
- when(mNetworkStatsManagerInternal.getNetworkTotalBytes(
- any(),
+ // Mock stats for today.
+ when(mStatsManager.querySummaryForDevice(any(),
eq(startOfDay.toInstant().toEpochMilli()),
- eq(now.toInstant().toEpochMilli()))).thenReturn(usedBytesToday);
+ eq(now.toInstant().toEpochMilli()))).thenReturn(mockedStatsBucket);
ArgumentCaptor<ConnectivityManager.NetworkCallback> networkCallback =
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
@@ -289,8 +304,10 @@
testGetMultipathPreference(
DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
OPPORTUNISTIC_QUOTA_UNKNOWN,
- // 29 days from Apr. 2nd to May 1st
- DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyWarning */,
+ // Remaining days are 29 days from Apr. 2nd to May 1st.
+ // Set limit so that 15MB * remaining days will be 5% of the remaining limit,
+ // so it will be 15 * 29 / 0.05 + used bytes.
+ DataUnit.MEGABYTES.toBytes(15 * 29 * 20 + 7) /* policyWarning */,
LIMIT_DISABLED,
DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
2_500_000 /* defaultResSetting */,
@@ -306,9 +323,11 @@
testGetMultipathPreference(
DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
OPPORTUNISTIC_QUOTA_UNKNOWN,
- // 29 days from Apr. 2nd to May 1st
POLICY_SNOOZED /* policyWarning */,
- DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyLimit */,
+ // Remaining days are 29 days from Apr. 2nd to May 1st.
+ // Set limit so that 15MB * remaining days will be 5% of the remaining limit,
+ // so it will be 15 * 29 / 0.05 + used bytes.
+ DataUnit.MEGABYTES.toBytes(15 * 29 * 20 + 7) /* policyLimit */,
DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
2_500_000 /* defaultResSetting */,
false /* roaming */);
diff --git a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
index f358726..aa4c4e3 100644
--- a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -109,8 +109,8 @@
mNai.linkProperties = new LinkProperties();
mNai.linkProperties.setInterfaceName(BASE_IFACE);
- mNai.networkInfo = new NetworkInfo(null);
- mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+ mNai.networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */,
+ null /* typeName */, null /* subtypeName */);
mNai.networkCapabilities = new NetworkCapabilities();
markNetworkConnected();
when(mNai.connService()).thenReturn(mConnectivity);
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index ecda338..99ef80b 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -60,6 +60,7 @@
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -118,10 +119,13 @@
private static final int SYSTEM_APPID1 = 1100;
private static final int SYSTEM_APPID2 = 1108;
private static final int VPN_APPID = 10002;
- private static final int MOCK_UID1 = MOCK_USER1.getUid(MOCK_APPID1);
- private static final int MOCK_UID2 = MOCK_USER1.getUid(MOCK_APPID2);
- private static final int SYSTEM_APP_UID1 = MOCK_USER1.getUid(SYSTEM_APPID1);
+ private static final int MOCK_UID11 = MOCK_USER1.getUid(MOCK_APPID1);
+ private static final int MOCK_UID12 = MOCK_USER1.getUid(MOCK_APPID2);
+ private static final int SYSTEM_APP_UID11 = MOCK_USER1.getUid(SYSTEM_APPID1);
private static final int VPN_UID = MOCK_USER1.getUid(VPN_APPID);
+ private static final int MOCK_UID21 = MOCK_USER2.getUid(MOCK_APPID1);
+ private static final int MOCK_UID22 = MOCK_USER2.getUid(MOCK_APPID2);
+ private static final int SYSTEM_APP_UID21 = MOCK_USER2.getUid(SYSTEM_APPID1);
private static final String REAL_SYSTEM_PACKAGE_NAME = "android";
private static final String MOCK_PACKAGE1 = "appName1";
private static final String MOCK_PACKAGE2 = "appName2";
@@ -161,9 +165,13 @@
doCallRealMethod().when(mContext).getSystemService(SystemConfigManager.class);
}
when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
- final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
- doReturn(UserHandle.ALL).when(asUserCtx).getUser();
- when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
+ doAnswer(invocation -> {
+ final Object[] args = invocation.getArguments();
+ final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+ final UserHandle user = (UserHandle) args[0];
+ doReturn(user).when(asUserCtx).getUser();
+ return asUserCtx;
+ }).when(mContext).createContextAsUser(any(), anyInt());
when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of());
// Set DEVICE_INITIAL_SDK_INT to Q that SYSTEM_UID won't have restricted network permission
// by default.
@@ -172,7 +180,7 @@
mPermissionMonitor = new PermissionMonitor(mContext, mNetdService, mDeps);
mNetdMonitor = new NetdMonitor(mNetdService);
- doReturn(List.of()).when(mPackageManager).getInstalledPackages(anyInt());
+ doReturn(List.of()).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
}
private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion,
@@ -330,26 +338,26 @@
@Test
public void testHasRestrictedNetworkPermission() {
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, NETWORK_STACK));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, NETWORK_STACK));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1,
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CHANGE_WIFI_STATE));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_WIFI_STATE));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID1,
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
PERMISSION_MAINLINE_NETWORK_STACK));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
+ PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
}
@Test
@@ -376,43 +384,43 @@
@Test
public void testHasRestrictedNetworkPermissionVendorApp() {
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, NETWORK_STACK));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, NETWORK_STACK));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1,
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID1, CHANGE_WIFI_STATE));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_WIFI_STATE));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
}
@Test
public void testHasRestrictedNetworkPermissionUidAllowedOnRestrictedNetworks() {
- mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID1));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID11));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12, CONNECTIVITY_INTERNAL));
}
@@ -429,27 +437,27 @@
doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID11));
+ assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID11));
assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID11));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID11));
doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID11));
+ assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID11));
assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID11));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID11));
assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, SYSTEM_UID));
assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID11));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID11));
}
private boolean wouldBeUidAllowedOnRestrictedNetworks(int uid) {
@@ -461,20 +469,20 @@
@Test
public void testIsAppAllowedOnRestrictedNetworks() {
mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of());
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
- mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID1));
- assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID11));
+ assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
- mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID2));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
- assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID12));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+ assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(123));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
}
private void assertBackgroundPermission(boolean hasPermission, String name, int uid,
@@ -491,16 +499,16 @@
assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE);
assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK);
- assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1));
- assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID1);
- assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID1,
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID11));
+ assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID11);
+ assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
- assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
- assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2);
- assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2,
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID12));
+ assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID12);
+ assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID12,
CONNECTIVITY_INTERNAL);
- assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK);
+ assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID12, NETWORK_STACK);
}
private class NetdMonitor {
@@ -589,13 +597,13 @@
@Test
public void testUserAndPackageAddRemove() throws Exception {
- // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
- // SYSTEM_APP_UID1: SYSTEM_PACKAGE1 has system permission.
- // SYSTEM_APP_UID1: SYSTEM_PACKAGE2 only has network permission.
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
- buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE1, SYSTEM_APP_UID1,
+ // MOCK_UID11: MOCK_PACKAGE1 only has network permission.
+ // SYSTEM_APP_UID11: SYSTEM_PACKAGE1 has system permission.
+ // SYSTEM_APP_UID11: SYSTEM_PACKAGE2 only has network permission.
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+ buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE1, SYSTEM_APP_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
- buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE2, SYSTEM_APP_UID1,
+ buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE2, SYSTEM_APP_UID11,
CHANGE_NETWORK_STATE);
// Add user MOCK_USER1.
@@ -610,16 +618,19 @@
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
SYSTEM_APPID1);
+ final List<PackageInfo> pkgs = List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID21,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(SYSTEM_PACKAGE2, SYSTEM_APP_UID21, CHANGE_NETWORK_STATE));
+ doReturn(pkgs).when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS),
+ eq(MOCK_USER_ID2));
// Add user MOCK_USER2.
mPermissionMonitor.onUserAdded(MOCK_USER2);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
SYSTEM_APPID1);
// Remove SYSTEM_PACKAGE2, expect keep system permission.
- when(mPackageManager.getPackagesForUid(SYSTEM_APP_UID1))
- .thenReturn(new String[]{SYSTEM_PACKAGE1});
- when(mPackageManager.getPackagesForUid(MOCK_USER2.getUid(SYSTEM_APPID1)))
- .thenReturn(new String[]{SYSTEM_PACKAGE1});
+ doReturn(new String[]{SYSTEM_PACKAGE1}).when(mPackageManager)
+ .getPackagesForUid(intThat(uid -> UserHandle.getAppId(uid) == SYSTEM_APPID1));
removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2},
SYSTEM_PACKAGE2, SYSTEM_APPID1);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
@@ -631,6 +642,8 @@
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
SYSTEM_APPID1);
+ // Add MOCK_PACKAGE1
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID21, CHANGE_NETWORK_STATE);
addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
SYSTEM_APPID1);
@@ -638,9 +651,8 @@
MOCK_APPID1);
// Remove MOCK_PACKAGE1, expect no permission left for all user.
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
- when(mPackageManager.getPackagesForUid(MOCK_USER2.getUid(MOCK_APPID1)))
- .thenReturn(new String[]{});
+ doReturn(new String[]{}).when(mPackageManager)
+ .getPackagesForUid(intThat(uid -> UserHandle.getAppId(uid) == MOCK_APPID1));
removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1);
@@ -674,68 +686,71 @@
@Test
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID1, CHANGE_NETWORK_STATE,
- CONNECTIVITY_USE_RESTRICTED_NETWORKS),
- buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
- buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2),
- buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
+ doReturn(List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID12),
+ buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
mPermissionMonitor.startMonitoring();
- // Every app on user 0 except MOCK_UID2 are under VPN.
+ // Every app on user 0 except MOCK_UID12 are under VPN.
final Set<UidRange> vpnRange1 = Set.of(
- new UidRange(0, MOCK_UID2 - 1),
- new UidRange(MOCK_UID2 + 1, UserHandle.PER_USER_RANGE - 1));
- final Set<UidRange> vpnRange2 = Set.of(new UidRange(MOCK_UID2, MOCK_UID2));
+ new UidRange(0, MOCK_UID12 - 1),
+ new UidRange(MOCK_UID12 + 1, UserHandle.PER_USER_RANGE - 1));
+ final Set<UidRange> vpnRange2 = Set.of(new UidRange(MOCK_UID12, MOCK_UID12));
- // When VPN is connected, expect a rule to be set up for user app MOCK_UID1
+ // When VPN is connected, expect a rule to be set up for user app MOCK_UID11
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange1, VPN_UID);
- verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID1}));
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
reset(mNetdService);
- // When MOCK_UID1 package is uninstalled and reinstalled, expect Netd to be updated
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID1}));
- mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_UID1);
- verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID1}));
+ // When MOCK_UID11 package is uninstalled and reinstalled, expect Netd to be updated
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+ verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID11}));
+ mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_UID11);
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
reset(mNetdService);
// During VPN uid update (vpnRange1 -> vpnRange2), ConnectivityService first deletes the
// old UID rules then adds the new ones. Expect netd to be updated
mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange1, VPN_UID);
- verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
+ verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID11}));
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange2, VPN_UID);
- verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID2}));
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID12}));
reset(mNetdService);
// When VPN is disconnected, expect rules to be torn down
mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange2, VPN_UID);
- verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID2}));
+ verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID12}));
assertNull(mPermissionMonitor.getVpnUidRanges("tun0"));
}
@Test
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID1, CHANGE_NETWORK_STATE,
- NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS),
- buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
+ doReturn(List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
+ NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
mPermissionMonitor.startMonitoring();
- final Set<UidRange> vpnRange = Set.of(UidRange.createForUser(MOCK_USER1));
+ final Set<UidRange> vpnRange = Set.of(UidRange.createForUser(MOCK_USER1),
+ UidRange.createForUser(MOCK_USER2));
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange, VPN_UID);
// Newly-installed package should have uid rules added
- mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_UID1);
- verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID1}));
+ addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID21}));
// Removed package should have its uid rules removed
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+ verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID11}));
+ verify(mNetdService, never()).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID21}));
}
@@ -767,7 +782,7 @@
netdPermissionsAppIds.put(SYSTEM_APPID2, PERMISSION_UPDATE_DEVICE_STATS);
// Send the permission information to netd, expect permission updated.
- mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds);
+ mPermissionMonitor.sendAppIdsTrafficPermission(netdPermissionsAppIds);
mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID2);
@@ -790,69 +805,69 @@
@Test
public void testPackageInstall() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- addPackage(MOCK_PACKAGE2, MOCK_UID2, INTERNET);
+ addPackage(MOCK_PACKAGE2, MOCK_UID12, INTERNET);
mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID2);
}
@Test
public void testPackageInstallSharedUid() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
// Install another package with the same uid and no permissions should not cause the app id
// to lose permissions.
- addPackage(MOCK_PACKAGE2, MOCK_UID1);
+ addPackage(MOCK_PACKAGE2, MOCK_UID11);
mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
}
@Test
public void testPackageUninstallBasic() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
}
@Test
public void testPackageRemoveThenAdd() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
- addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
}
@Test
public void testPackageUpdate() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11);
mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1);
- addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
}
@Test
public void testPackageUninstallWithMultiplePackages() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, INTERNET, UPDATE_DEVICE_STATS);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
// Install another package with the same uid but different permissions.
- addPackage(MOCK_PACKAGE2, MOCK_UID1, INTERNET);
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ addPackage(MOCK_PACKAGE2, MOCK_UID11, INTERNET);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID11);
// Uninstall MOCK_PACKAGE1 and expect only INTERNET permission left.
- when(mPackageManager.getPackagesForUid(eq(MOCK_UID1)))
+ when(mPackageManager.getPackagesForUid(eq(MOCK_UID11)))
.thenReturn(new String[]{MOCK_PACKAGE2});
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
}
@@ -871,9 +886,9 @@
@Test
public void testUpdateUidPermissionsFromSystemConfig() throws Exception {
when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET)))
- .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 });
+ .thenReturn(new int[]{ MOCK_UID11, MOCK_UID12 });
when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS)))
- .thenReturn(new int[]{ MOCK_UID2 });
+ .thenReturn(new int[]{ MOCK_UID12 });
mPermissionMonitor.startMonitoring();
mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
@@ -904,17 +919,17 @@
// Verify receiving PACKAGE_ADDED intent.
final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
- addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, INTERNET,
+ addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID11);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, INTERNET,
UPDATE_DEVICE_STATS);
receiver.onReceive(mContext, addedIntent);
mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
// Verify receiving PACKAGE_REMOVED intent.
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
- removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+ removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID11);
receiver.onReceive(mContext, removedIntent);
mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
}
@@ -934,20 +949,20 @@
Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
// Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12);
- // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
+ // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11
// should have SYSTEM permission.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1));
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
MOCK_APPID1);
mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
- // MOCK_UID2 is listed in setting that allow to use restricted networks, MOCK_UID2
- // should have SYSTEM permission but MOCK_UID1 should revoke permission.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID2));
+ // MOCK_UID12 is listed in setting that allow to use restricted networks, MOCK_UID12
+ // should have SYSTEM permission but MOCK_UID11 should revoke permission.
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID12));
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
MOCK_APPID2);
@@ -965,29 +980,29 @@
final ContentObserver contentObserver = expectRegisterContentObserver(
Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11);
- // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID1 should have NETWORK permission.
+ // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID11 should have NETWORK permission.
addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_APPID1);
mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
MOCK_APPID1);
- // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
+ // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11
// should upgrade to SYSTEM permission.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1));
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
MOCK_APPID1);
- // No app lists in setting, MOCK_UID1 should downgrade to NETWORK permission.
+ // No app lists in setting, MOCK_UID11 should downgrade to NETWORK permission.
when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of());
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
MOCK_APPID1);
- // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID1.
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{MOCK_PACKAGE2});
+ // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID11.
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{MOCK_PACKAGE2});
removePackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_APPID1);
mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1);
}
@@ -998,33 +1013,39 @@
final ContentObserver contentObserver = expectRegisterContentObserver(
Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
- // Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2.
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2);
+ // Prepare PackageInfo for MOCK_APPID1 and MOCK_APPID2 in MOCK_USER1.
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12);
- // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
- // should have SYSTEM permission and MOCK_UID2 has no permissions.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1));
+ // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11 should
+ // have SYSTEM permission and MOCK_UID12 has no permissions.
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
contentObserver.onChange(true /* selfChange */);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
MOCK_APPID1);
mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
// Add user MOCK_USER2.
+ final List<PackageInfo> pkgs = List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID21));
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID22);
+ doReturn(pkgs).when(mPackageManager)
+ .getInstalledPackagesAsUser(eq(GET_PERMISSIONS), eq(MOCK_USER_ID2));
mPermissionMonitor.onUserAdded(MOCK_USER2);
- // MOCK_APPID1 in both users should all have SYSTEM permission and MOCK_APPID2 has no
- // permissions in either user.
- mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ // MOCK_APPID1 in MOCK_USER1 should have SYSTEM permission but in MOCK_USER2 should have no
+ // permissions. And MOCK_APPID2 has no permissions in either users.
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
MOCK_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_APPID1);
mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID2);
- // MOCK_UID2 is listed in setting that allow to use restricted networks, MOCK_APPID2
- // in both users should have SYSTEM permission and MOCK_APPID1 has no permissions in either
- // user.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID2));
+ // MOCK_UID22 is listed in setting that allow to use restricted networks,
+ // MOCK_APPID2 in MOCK_USER2 should have SYSTEM permission but in MOCK_USER1 should have no
+ // permissions. And MOCK_APPID1 has no permissions in either users.
+ doReturn(Set.of(MOCK_UID22)).when(mDeps).getUidsAllowedOnRestrictedNetworks(any());
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER2},
MOCK_APPID2);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1);
// Remove user MOCK_USER1
@@ -1044,9 +1065,9 @@
public void testOnExternalApplicationsAvailable() throws Exception {
// Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
// and have different uids. There has no permission for both uids.
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
- buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2)));
+ doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID12)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
mPermissionMonitor.startMonitoring();
mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1, MOCK_APPID2);
mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1, MOCK_APPID2);
@@ -1057,9 +1078,9 @@
final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST,
new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2});
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE,
UPDATE_DEVICE_STATS);
receiver.onReceive(mContext, externalIntent);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
@@ -1079,9 +1100,9 @@
// Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
// and have different uids. There has no permission for both uids.
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE,
UPDATE_DEVICE_STATS);
// Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
@@ -1101,10 +1122,10 @@
public void testOnExternalApplicationsAvailableWithSharedUid()
throws Exception {
// Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
- // storage and shared on MOCK_UID1. There has no permission for MOCK_UID1.
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
- buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1)));
+ // storage and shared on MOCK_UID11. There has no permission for MOCK_UID11.
+ doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID11)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
mPermissionMonitor.startMonitoring();
mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1);
mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1);
@@ -1114,8 +1135,8 @@
// Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1});
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, UPDATE_DEVICE_STATS);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11, UPDATE_DEVICE_STATS);
receiver.onReceive(mContext, externalIntent);
mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
MOCK_APPID1);
@@ -1126,12 +1147,11 @@
public void testOnExternalApplicationsAvailableWithSharedUid_DifferentStorage()
throws Exception {
// Initial the permission state. MOCK_PACKAGE1 is installed on external storage and
- // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID1.
- // MOCK_UID1 has NETWORK and INTERNET permissions.
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
- buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE,
- INTERNET)));
+ // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID11.
+ // MOCK_UID11 has NETWORK and INTERNET permissions.
+ doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID11, CHANGE_NETWORK_STATE, INTERNET)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
mPermissionMonitor.startMonitoring();
mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
MOCK_APPID1);
@@ -1142,9 +1162,9 @@
// Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1});
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS, UPDATE_DEVICE_STATS);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11, CHANGE_NETWORK_STATE,
INTERNET);
receiver.onReceive(mContext, externalIntent);
mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
diff --git a/tests/unit/java/com/android/server/connectivity/UidRangeUtilsTest.java b/tests/unit/java/com/android/server/connectivity/UidRangeUtilsTest.java
new file mode 100644
index 0000000..b8c2673
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/UidRangeUtilsTest.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.UidRange;
+import android.os.Build;
+import android.util.ArraySet;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tests for UidRangeUtils.
+ *
+ * Build, install and run with:
+ * runtest frameworks-net -c com.android.server.connectivity.UidRangeUtilsTest
+ */
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+public class UidRangeUtilsTest {
+ private static void assertInSameRange(@NonNull final String msg,
+ @Nullable final UidRange r1,
+ @Nullable final Set<UidRange> s2) {
+ assertTrue(msg + " : " + s2 + " unexpectedly is not in range of " + r1,
+ UidRangeUtils.isRangeSetInUidRange(r1, s2));
+ }
+
+ private static void assertNotInSameRange(@NonNull final String msg,
+ @Nullable final UidRange r1, @Nullable final Set<UidRange> s2) {
+ assertFalse(msg + " : " + s2 + " unexpectedly is in range of " + r1,
+ UidRangeUtils.isRangeSetInUidRange(r1, s2));
+ }
+
+ @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRangeSetInUidRange() {
+ final UidRange uids1 = new UidRange(1, 100);
+ final UidRange uids2 = new UidRange(3, 300);
+ final UidRange uids3 = new UidRange(1, 1000);
+ final UidRange uids4 = new UidRange(1, 100);
+ final UidRange uids5 = new UidRange(2, 20);
+ final UidRange uids6 = new UidRange(3, 30);
+
+ assertThrows(NullPointerException.class,
+ () -> UidRangeUtils.isRangeSetInUidRange(null, null));
+ assertThrows(NullPointerException.class,
+ () -> UidRangeUtils.isRangeSetInUidRange(uids1, null));
+
+ final ArraySet<UidRange> set1 = new ArraySet<>();
+ final ArraySet<UidRange> set2 = new ArraySet<>();
+
+ assertThrows(NullPointerException.class,
+ () -> UidRangeUtils.isRangeSetInUidRange(null, set1));
+ assertInSameRange("uids1 <=> empty", uids1, set2);
+
+ set2.add(uids1);
+ assertInSameRange("uids1 <=> uids1", uids1, set2);
+
+ set2.clear();
+ set2.add(uids2);
+ assertNotInSameRange("uids1 <=> uids2", uids1, set2);
+ set2.clear();
+ set2.add(uids3);
+ assertNotInSameRange("uids1 <=> uids3", uids1, set2);
+ set2.clear();
+ set2.add(uids4);
+ assertInSameRange("uids1 <=> uids4", uids1, set2);
+
+ set2.clear();
+ set2.add(uids5);
+ set2.add(uids6);
+ assertInSameRange("uids1 <=> uids5, 6", uids1, set2);
+
+ set2.clear();
+ set2.add(uids2);
+ set2.add(uids6);
+ assertNotInSameRange("uids1 <=> uids2, 6", uids1, set2);
+ }
+
+ @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRemoveRangeSetFromUidRange() {
+ final UidRange uids1 = new UidRange(1, 100);
+ final UidRange uids2 = new UidRange(3, 300);
+ final UidRange uids3 = new UidRange(1, 1000);
+ final UidRange uids4 = new UidRange(1, 100);
+ final UidRange uids5 = new UidRange(2, 20);
+ final UidRange uids6 = new UidRange(3, 30);
+ final UidRange uids7 = new UidRange(30, 39);
+
+ final UidRange uids8 = new UidRange(1, 1);
+ final UidRange uids9 = new UidRange(21, 100);
+ final UidRange uids10 = new UidRange(1, 2);
+ final UidRange uids11 = new UidRange(31, 100);
+
+ final UidRange uids12 = new UidRange(1, 1);
+ final UidRange uids13 = new UidRange(21, 29);
+ final UidRange uids14 = new UidRange(40, 100);
+
+ final UidRange uids15 = new UidRange(3, 30);
+ final UidRange uids16 = new UidRange(31, 39);
+
+ assertThrows(NullPointerException.class,
+ () -> UidRangeUtils.removeRangeSetFromUidRange(null, null));
+ Set<UidRange> expected = new ArraySet<>();
+ expected.add(uids1);
+ assertThrows(NullPointerException.class,
+ () -> UidRangeUtils.removeRangeSetFromUidRange(uids1, null));
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, new ArraySet<>()));
+
+ expected.clear();
+ final ArraySet<UidRange> set2 = new ArraySet<>();
+ set2.add(uids1);
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+ set2.clear();
+ set2.add(uids4);
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+
+ expected.add(uids10);
+ set2.clear();
+ set2.add(uids2);
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+
+ expected.clear();
+ set2.clear();
+ set2.add(uids3);
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+
+ set2.clear();
+ set2.add(uids3);
+ set2.add(uids6);
+ assertThrows(IllegalArgumentException.class,
+ () -> UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+
+ expected.clear();
+ expected.add(uids8);
+ expected.add(uids9);
+ set2.clear();
+ set2.add(uids5);
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+
+ expected.clear();
+ expected.add(uids10);
+ expected.add(uids11);
+ set2.clear();
+ set2.add(uids6);
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+
+ expected.clear();
+ expected.add(uids12);
+ expected.add(uids13);
+ expected.add(uids14);
+ set2.clear();
+ set2.add(uids5);
+ set2.add(uids7);
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+
+ expected.clear();
+ expected.add(uids10);
+ expected.add(uids14);
+ set2.clear();
+ set2.add(uids15);
+ set2.add(uids16);
+ assertEquals(expected, UidRangeUtils.removeRangeSetFromUidRange(uids1, set2));
+ }
+
+ private static void assertRangeOverlaps(@NonNull final String msg,
+ @Nullable final Set<UidRange> s1,
+ @Nullable final Set<UidRange> s2) {
+ assertTrue(msg + " : " + s2 + " unexpectedly does not overlap with " + s1,
+ UidRangeUtils.doesRangeSetOverlap(s1, s2));
+ }
+
+ private static void assertRangeDoesNotOverlap(@NonNull final String msg,
+ @Nullable final Set<UidRange> s1, @Nullable final Set<UidRange> s2) {
+ assertFalse(msg + " : " + s2 + " unexpectedly ovelaps with " + s1,
+ UidRangeUtils.doesRangeSetOverlap(s1, s2));
+ }
+
+ @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRangeSetOverlap() {
+ final UidRange uids1 = new UidRange(1, 100);
+ final UidRange uids2 = new UidRange(3, 300);
+ final UidRange uids3 = new UidRange(1, 1000);
+ final UidRange uids4 = new UidRange(1, 100);
+ final UidRange uids5 = new UidRange(2, 20);
+ final UidRange uids6 = new UidRange(3, 30);
+ final UidRange uids7 = new UidRange(0, 0);
+ final UidRange uids8 = new UidRange(1, 500);
+ final UidRange uids9 = new UidRange(101, 200);
+
+ assertThrows(NullPointerException.class,
+ () -> UidRangeUtils.doesRangeSetOverlap(null, null));
+
+ final ArraySet<UidRange> set1 = new ArraySet<>();
+ final ArraySet<UidRange> set2 = new ArraySet<>();
+ assertThrows(NullPointerException.class,
+ () -> UidRangeUtils.doesRangeSetOverlap(set1, null));
+ assertThrows(NullPointerException.class,
+ () -> UidRangeUtils.doesRangeSetOverlap(null, set2));
+ assertRangeDoesNotOverlap("empty <=> null", set1, set2);
+
+ set2.add(uids1);
+ set1.add(uids1);
+ assertRangeOverlaps("uids1 <=> uids1", set1, set2);
+
+ set1.clear();
+ set1.add(uids1);
+ set2.clear();
+ set2.add(uids2);
+ assertRangeOverlaps("uids1 <=> uids2", set1, set2);
+
+ set1.clear();
+ set1.add(uids1);
+ set2.clear();
+ set2.add(uids3);
+ assertRangeOverlaps("uids1 <=> uids3", set1, set2);
+
+ set1.clear();
+ set1.add(uids1);
+ set2.clear();
+ set2.add(uids4);
+ assertRangeOverlaps("uids1 <=> uids4", set1, set2);
+
+ set1.clear();
+ set1.add(uids1);
+ set2.clear();
+ set2.add(uids5);
+ set2.add(uids6);
+ assertRangeOverlaps("uids1 <=> uids5,6", set1, set2);
+
+ set1.clear();
+ set1.add(uids1);
+ set2.clear();
+ set2.add(uids7);
+ assertRangeDoesNotOverlap("uids1 <=> uids7", set1, set2);
+
+ set1.clear();
+ set1.add(uids1);
+ set2.clear();
+ set2.add(uids9);
+ assertRangeDoesNotOverlap("uids1 <=> uids9", set1, set2);
+
+ set1.clear();
+ set1.add(uids1);
+ set2.clear();
+ set2.add(uids8);
+ assertRangeOverlaps("uids1 <=> uids8", set1, set2);
+
+
+ set1.clear();
+ set1.add(uids1);
+ set2.clear();
+ set2.add(uids8);
+ set2.add(uids7);
+ assertRangeOverlaps("uids1 <=> uids7, 8", set1, set2);
+ }
+
+ @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testConvertListToUidRange() {
+ final UidRange uids1 = new UidRange(1, 1);
+ final UidRange uids2 = new UidRange(1, 2);
+ final UidRange uids3 = new UidRange(100, 100);
+ final UidRange uids4 = new UidRange(10, 10);
+
+ final UidRange uids5 = new UidRange(10, 14);
+ final UidRange uids6 = new UidRange(20, 24);
+
+ final Set<UidRange> expected = new ArraySet<>();
+ final List<Integer> input = new ArrayList<Integer>();
+
+ assertThrows(NullPointerException.class, () -> UidRangeUtils.convertListToUidRange(null));
+ assertEquals(expected, UidRangeUtils.convertListToUidRange(input));
+
+ input.add(1);
+ expected.add(uids1);
+ assertEquals(expected, UidRangeUtils.convertListToUidRange(input));
+
+ input.add(2);
+ expected.clear();
+ expected.add(uids2);
+ assertEquals(expected, UidRangeUtils.convertListToUidRange(input));
+
+ input.clear();
+ input.add(1);
+ input.add(100);
+ expected.clear();
+ expected.add(uids1);
+ expected.add(uids3);
+ assertEquals(expected, UidRangeUtils.convertListToUidRange(input));
+
+ input.clear();
+ input.add(100);
+ input.add(1);
+ expected.clear();
+ expected.add(uids1);
+ expected.add(uids3);
+ assertEquals(expected, UidRangeUtils.convertListToUidRange(input));
+
+ input.clear();
+ input.add(100);
+ input.add(1);
+ input.add(2);
+ input.add(1);
+ input.add(10);
+ expected.clear();
+ expected.add(uids2);
+ expected.add(uids4);
+ expected.add(uids3);
+ assertEquals(expected, UidRangeUtils.convertListToUidRange(input));
+
+ input.clear();
+ input.add(10);
+ input.add(11);
+ input.add(12);
+ input.add(13);
+ input.add(14);
+ input.add(20);
+ input.add(21);
+ input.add(22);
+ input.add(23);
+ input.add(24);
+ expected.clear();
+ expected.add(uids5);
+ expected.add(uids6);
+ assertEquals(expected, UidRangeUtils.convertListToUidRange(input));
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index b706090..fd9aefa 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import static android.Manifest.permission.BIND_VPN_SERVICE;
import static android.content.pm.UserInfo.FLAG_ADMIN;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
@@ -31,12 +32,14 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
@@ -70,6 +73,7 @@
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.Network;
@@ -86,6 +90,7 @@
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.INetworkManagementService;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -102,6 +107,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.modules.utils.build.SdkLevel;
import com.android.server.IpSecService;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -118,6 +124,7 @@
import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileWriter;
import java.io.IOException;
import java.net.Inet4Address;
@@ -851,6 +858,81 @@
}
@Test
+ public void testStartOpAndFinishOpWillBeCalledWhenPlatformVpnIsOnAndOff() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastT());
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verify(mAppOps).noteOpNoThrow(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ verify(mAppOps).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ // Add a small delay to make sure that startOp is only called once.
+ verify(mAppOps, after(100).times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ // Check that the startOp is not called with OPSTR_ESTABLISH_VPN_SERVICE.
+ verify(mAppOps, never()).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+ // Add a small delay to double confirm that startOp is only called once.
+ verify(mAppOps, after(100)).finishOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */);
+ }
+
+ @Test
+ public void testStartOpWithSeamlessHandover() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastT());
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_VPN);
+ assertTrue(vpn.prepare(TEST_VPN_PKG, null, VpnManager.TYPE_VPN_SERVICE));
+ final VpnConfig config = new VpnConfig();
+ config.user = "VpnTest";
+ config.addresses.add(new LinkAddress("192.0.2.2/32"));
+ config.mtu = 1450;
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.permission = BIND_VPN_SERVICE;
+ resolveInfo.serviceInfo = serviceInfo;
+ when(mPackageManager.resolveService(any(), anyInt())).thenReturn(resolveInfo);
+ when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+ vpn.establish(config);
+ verify(mAppOps, times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ // Call establish() twice with the same config, it should match seamless handover case and
+ // startOp() shouldn't be called again.
+ vpn.establish(config);
+ verify(mAppOps, times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ }
+
+ @Test
public void testSetPackageAuthorizationVpnService() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
@@ -1197,6 +1279,32 @@
public boolean isInterfacePresent(final Vpn vpn, final String iface) {
return true;
}
+
+ @Override
+ public ParcelFileDescriptor adoptFd(Vpn vpn, int mtu) {
+ return new ParcelFileDescriptor(new FileDescriptor());
+ }
+
+ @Override
+ public int jniCreate(Vpn vpn, int mtu) {
+ // Pick a random positive number as fd to return.
+ return 345;
+ }
+
+ @Override
+ public String jniGetName(Vpn vpn, int fd) {
+ return TEST_IFACE_NAME;
+ }
+
+ @Override
+ public int jniSetAddresses(Vpn vpn, String interfaze, String addresses) {
+ if (addresses == null) return 0;
+ // Return the number of addresses.
+ return addresses.split(" ").length;
+ }
+
+ @Override
+ public void setBlocking(FileDescriptor fd, boolean blocking) {}
}
/**
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index 8d7aa4e..8340a13 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -34,6 +34,7 @@
import static org.junit.Assert.fail;
import android.content.res.Resources;
+import android.net.INetd;
import android.net.NetworkStats;
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
@@ -54,6 +55,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.File;
import java.io.FileOutputStream;
@@ -70,16 +73,19 @@
private File mTestProc;
private NetworkStatsFactory mFactory;
+ @Mock
+ private INetd mNetd;
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mTestProc = TestIoUtils.createTemporaryDirectory("proc");
// The libandroid_servers which have the native method is not available to
// applications. So in order to have a test support native library, the native code
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni");
- mFactory = new NetworkStatsFactory(mTestProc, false);
+ mFactory = new NetworkStatsFactory(mTestProc, false, mNetd);
mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]);
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
index e35104e..d993d1f 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -29,6 +29,8 @@
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -37,9 +39,10 @@
import android.app.usage.NetworkStatsManager;
import android.net.DataUsageRequest;
import android.net.NetworkIdentity;
+import android.net.NetworkIdentitySet;
import android.net.NetworkStats;
+import android.net.NetworkStatsAccess;
import android.net.NetworkTemplate;
-import android.os.Build;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -72,7 +75,7 @@
*/
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
public class NetworkStatsObserversTest {
private static final String TEST_IFACE = "test0";
private static final String TEST_IFACE2 = "test1";
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index ab76460..5e1699a 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -41,7 +41,6 @@
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
-import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
@@ -50,7 +49,6 @@
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
import static android.net.NetworkTemplate.OEM_MANAGED_NO;
import static android.net.NetworkTemplate.OEM_MANAGED_YES;
-import static android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
import static android.net.NetworkTemplate.buildTemplateWifi;
@@ -63,19 +61,20 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
+import static com.android.net.module.util.NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -86,7 +85,7 @@
import android.content.Intent;
import android.database.ContentObserver;
import android.net.DataUsageRequest;
-import android.net.INetworkManagementEventObserver;
+import android.net.INetd;
import android.net.INetworkStatsSession;
import android.net.LinkProperties;
import android.net.Network;
@@ -96,14 +95,16 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
+import android.net.TetherStatsParcel;
+import android.net.TetheringManager;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.net.wifi.WifiInfo;
import android.os.Build;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
@@ -116,8 +117,9 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.net.module.util.LocationPermissionChecker;
+import com.android.server.net.NetworkStatsService.AlertObserver;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
import com.android.testutils.DevSdkIgnoreRule;
@@ -158,9 +160,9 @@
private static final String IMSI_1 = "310004";
private static final String IMSI_2 = "310260";
- private static final String TEST_SSID = "AndroidAP";
+ private static final String TEST_WIFI_NETWORK_KEY = "WifiNetworkKey";
- private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
+ private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_WIFI_NETWORK_KEY);
private static NetworkTemplate sTemplateCarrierWifi1 =
buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL, IMSI_1);
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
@@ -181,7 +183,9 @@
private File mStatsDir;
private MockContext mServiceContext;
private @Mock TelephonyManager mTelephonyManager;
- private @Mock INetworkManagementService mNetManager;
+ private static @Mock WifiInfo sWifiInfo;
+ private @Mock INetd mNetd;
+ private @Mock TetheringManager mTetheringManager;
private @Mock NetworkStatsFactory mStatsFactory;
private @Mock NetworkStatsSettings mSettings;
private @Mock IBinder mBinder;
@@ -189,12 +193,15 @@
@Mock
private NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
private HandlerThread mHandlerThread;
+ @Mock
+ private LocationPermissionChecker mLocationPermissionChecker;
private NetworkStatsService mService;
private INetworkStatsSession mSession;
- private INetworkManagementEventObserver mNetworkObserver;
+ private AlertObserver mAlertObserver;
private ContentObserver mContentObserver;
private Handler mHandler;
+ private TetheringManager.TetheringEventCallback mTetheringEventCallback;
private class MockContext extends BroadcastInterceptingContext {
private final Context mBaseContext;
@@ -207,6 +214,7 @@
@Override
public Object getSystemService(String name) {
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
+ if (Context.TETHERING_SERVICE.equals(name)) return mTetheringManager;
return mBaseContext.getSystemService(name);
}
@@ -237,11 +245,28 @@
return currentTimeMillis();
}
};
+
+ @NonNull
+ private static TetherStatsParcel buildTetherStatsParcel(String iface, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, int ifIndex) {
+ TetherStatsParcel parcel = new TetherStatsParcel();
+ parcel.iface = iface;
+ parcel.rxBytes = rxBytes;
+ parcel.rxPackets = rxPackets;
+ parcel.txBytes = txBytes;
+ parcel.txPackets = txPackets;
+ parcel.ifIndex = ifIndex;
+ return parcel;
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
final Context context = InstrumentationRegistry.getContext();
mServiceContext = new MockContext(context);
+ when(mLocationPermissionChecker.checkCallersLocationPermission(
+ any(), any(), anyInt(), anyBoolean(), any())).thenReturn(true);
+ when(sWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
mStatsDir = TestIoUtils.createTemporaryDirectory(getClass().getSimpleName());
PowerManager powerManager = (PowerManager) mServiceContext.getSystemService(
@@ -251,7 +276,7 @@
mHandlerThread = new HandlerThread("HandlerThread");
final NetworkStatsService.Dependencies deps = makeDependencies();
- mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock,
+ mService = new NetworkStatsService(mServiceContext, mNetd, mAlarmManager, wakeLock,
mClock, mSettings, mStatsFactory, new NetworkStatsObservers(), mStatsDir,
getBaseDir(mStatsDir), deps);
@@ -276,11 +301,18 @@
mSession = mService.openSession();
assertNotNull("openSession() failed", mSession);
- // catch INetworkManagementEventObserver during systemReady()
- ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
- ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
- verify(mNetManager).registerObserver(networkObserver.capture());
- mNetworkObserver = networkObserver.getValue();
+ // Catch AlertObserver during systemReady().
+ final ArgumentCaptor<AlertObserver> alertObserver =
+ ArgumentCaptor.forClass(AlertObserver.class);
+ verify(mNetd).registerUnsolicitedEventListener(alertObserver.capture());
+ mAlertObserver = alertObserver.getValue();
+
+ // Catch TetheringEventCallback during systemReady().
+ ArgumentCaptor<TetheringManager.TetheringEventCallback> tetheringEventCbCaptor =
+ ArgumentCaptor.forClass(TetheringManager.TetheringEventCallback.class);
+ verify(mTetheringManager).registerTetheringEventCallback(
+ any(), tetheringEventCbCaptor.capture());
+ mTetheringEventCallback = tetheringEventCbCaptor.getValue();
}
@NonNull
@@ -293,7 +325,7 @@
@Override
public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(
- @NonNull Context context, @NonNull Looper looper, @NonNull Executor executor,
+ @NonNull Context context, @NonNull Executor executor,
@NonNull NetworkStatsService service) {
return mNetworkStatsSubscriptionsMonitor;
@@ -306,6 +338,10 @@
return mContentObserver = super.makeContentObserver(handler, settings, monitor);
}
+ @Override
+ public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
+ return mLocationPermissionChecker;
+ }
};
}
@@ -314,7 +350,7 @@
mServiceContext = null;
mStatsDir = null;
- mNetManager = null;
+ mNetd = null;
mSettings = null;
mSession.close();
@@ -358,7 +394,7 @@
// verify service recorded history
assertNetworkTotal(sTemplateCarrierWifi1, 1024L, 1L, 2048L, 2L, 0);
- // verify service recorded history for wifi with SSID filter
+ // verify service recorded history for wifi with WiFi Network Key filter
assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
@@ -368,7 +404,7 @@
// verify service recorded history
assertNetworkTotal(sTemplateCarrierWifi1, 4096L, 4L, 8192L, 8L, 0);
- // verify service recorded history for wifi with SSID filter
+ // verify service recorded history for wifi with WiFi Network Key filter
assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0);
}
@@ -523,7 +559,7 @@
public void testUidStatsAcrossNetworks() throws Exception {
// pretend first mobile network comes online
expectDefaultSettings();
- NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1)};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildMobileState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -554,7 +590,7 @@
// disappearing, to verify we don't count backwards.
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
- states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_2)};
+ states = new NetworkStateSnapshot[] {buildMobileState(IMSI_2)};
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
.insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
@@ -657,13 +693,16 @@
@Test
public void testMobileStatsByRatType() throws Exception {
final NetworkTemplate template3g =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS,
+ METERED_YES);
final NetworkTemplate template4g =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE,
+ METERED_YES);
final NetworkTemplate template5g =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR,
+ METERED_YES);
final NetworkStateSnapshot[] states =
- new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
+ new NetworkStateSnapshot[]{buildMobileState(IMSI_1)};
// 3G network comes online.
expectNetworkStatsSummary(buildEmptyStats());
@@ -730,30 +769,72 @@
}
@Test
+ public void testMobileStatsMeteredness() throws Exception {
+ // Create metered 5g template.
+ final NetworkTemplate templateMetered5g =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR,
+ METERED_YES);
+ // Create non-metered 5g template
+ final NetworkTemplate templateNonMetered5g =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR, METERED_NO);
+
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ // Pretend that 5g mobile network comes online
+ final NetworkStateSnapshot[] mobileStates =
+ new NetworkStateSnapshot[] {buildMobileState(IMSI_1), buildMobileState(TEST_IFACE2,
+ IMSI_1, true /* isTemporarilyNotMetered */, false /* isRoaming */)};
+ setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_NR);
+ mService.notifyNetworkStatus(NETWORKS_MOBILE, mobileStates,
+ getActiveIface(mobileStates), new UnderlyingNetworkInfo[0]);
+
+ // Create some traffic
+ // Note that all traffic from NetworkManagementService is tagged as METERED_NO, ROAMING_NO
+ // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
+ // They are layered on top by inspecting the iface properties.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
+ .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 256, 3L, 128L, 5L, 0L));
+ forcePollAndWaitForIdle();
+
+ // Verify service recorded history.
+ assertUidTotal(templateMetered5g, UID_RED, 128L, 2L, 128L, 2L, 0);
+ assertUidTotal(templateNonMetered5g, UID_RED, 256, 3L, 128L, 5L, 0);
+ }
+
+ @Test
public void testMobileStatsOemManaged() throws Exception {
final NetworkTemplate templateOemPaid = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
- /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PAID,
- SUBSCRIBER_ID_MATCH_RULE_EXACT);
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null,
+ /*matchWifiNetworkKeys=*/new String[0], METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PAID, SUBSCRIBER_ID_MATCH_RULE_EXACT);
final NetworkTemplate templateOemPrivate = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
- /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PRIVATE,
- SUBSCRIBER_ID_MATCH_RULE_EXACT);
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null,
+ /*matchWifiNetworkKeys=*/new String[0], METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PRIVATE, SUBSCRIBER_ID_MATCH_RULE_EXACT);
final NetworkTemplate templateOemAll = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
- /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_PAID | OEM_PRIVATE, SUBSCRIBER_ID_MATCH_RULE_EXACT);
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null,
+ /*matchWifiNetworkKeys=*/new String[0], METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PAID | OEM_PRIVATE,
+ SUBSCRIBER_ID_MATCH_RULE_EXACT);
final NetworkTemplate templateOemYes = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
- /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_YES,
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null,
+ /*matchWifiNetworkKeys=*/new String[0], METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_YES,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
final NetworkTemplate templateOemNone = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
- /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_NO,
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null,
+ /*matchWifiNetworkKeys=*/new String[0], METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_NO,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
// OEM_PAID network comes online.
@@ -912,7 +993,7 @@
}
@Test
- public void testDetailedUidStats() throws Exception {
+ public void testUidStatsForTransport() throws Exception {
// pretend that network comes online
expectDefaultSettings();
NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
@@ -938,7 +1019,7 @@
.insertEntry(entry3));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
- NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
+ NetworkStats stats = mService.getUidStatsForTransport(NetworkCapabilities.TRANSPORT_WIFI);
assertEquals(3, stats.size());
entry1.operations = 1;
@@ -949,68 +1030,6 @@
}
@Test
- public void testDetailedUidStats_Filtered() throws Exception {
- // pretend that network comes online
- expectDefaultSettings();
-
- final String stackedIface = "stacked-test0";
- final LinkProperties stackedProp = new LinkProperties();
- stackedProp.setInterfaceName(stackedIface);
- final NetworkStateSnapshot wifiState = buildWifiState();
- wifiState.getLinkProperties().addStackedLink(stackedProp);
- NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {wifiState};
-
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
-
- mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states),
- new UnderlyingNetworkInfo[0]);
-
- NetworkStats.Entry uidStats = new NetworkStats.Entry(
- TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
- // Stacked on matching interface
- NetworkStats.Entry tetheredStats1 = new NetworkStats.Entry(
- stackedIface, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
- // Different interface
- NetworkStats.Entry tetheredStats2 = new NetworkStats.Entry(
- "otherif", UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
-
- final String[] ifaceFilter = new String[] { TEST_IFACE };
- final String[] augmentedIfaceFilter = new String[] { stackedIface, TEST_IFACE };
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- when(mStatsFactory.augmentWithStackedInterfaces(eq(ifaceFilter)))
- .thenReturn(augmentedIfaceFilter);
- when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL)))
- .thenReturn(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(uidStats));
- when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
- .thenReturn(new NetworkStats(getElapsedRealtime(), 2)
- .insertEntry(tetheredStats1)
- .insertEntry(tetheredStats2));
-
- NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
-
- // mStatsFactory#readNetworkStatsDetail() has the following invocations:
- // 1) NetworkStatsService#systemReady from #setUp.
- // 2) mService#notifyNetworkStatus in the test above.
- //
- // Additionally, we should have one call from the above call to mService#getDetailedUidStats
- // with the augmented ifaceFilter.
- verify(mStatsFactory, times(2)).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
- verify(mStatsFactory, times(1)).readNetworkStatsDetail(
- eq(UID_ALL),
- eq(augmentedIfaceFilter),
- eq(TAG_ALL));
- assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE));
- assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface));
- assertEquals(2, stats.size());
- assertEquals(uidStats, stats.getValues(0, null));
- assertEquals(tetheredStats1, stats.getValues(1, null));
- }
-
- @Test
public void testForegroundBackground() throws Exception {
// pretend that network comes online
expectDefaultSettings();
@@ -1112,7 +1131,8 @@
// pretend that network comes online
expectDefaultSettings();
NetworkStateSnapshot[] states =
- new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1, true /* isRoaming */)};
+ new NetworkStateSnapshot[] {buildMobileState(TEST_IFACE, IMSI_1,
+ false /* isTemporarilyNotMetered */, true /* isRoaming */)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1151,7 +1171,7 @@
// pretend first mobile network comes online
expectDefaultSettings();
final NetworkStateSnapshot[] states =
- new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
+ new NetworkStateSnapshot[]{buildMobileState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1189,12 +1209,11 @@
final NetworkStats localUidStats = new NetworkStats(now, 1)
.insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
// Software per-uid tethering traffic.
- final NetworkStats tetherSwUidStats = new NetworkStats(now, 1)
- .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1408L, 10L, 256L, 1L,
- 0L);
+ final TetherStatsParcel[] tetherStatsParcels =
+ {buildTetherStatsParcel(TEST_IFACE, 1408L, 10L, 256L, 1L, 0)};
expectNetworkStatsSummary(swIfaceStats);
- expectNetworkStatsUidDetail(localUidStats, tetherSwUidStats);
+ expectNetworkStatsUidDetail(localUidStats, tetherStatsParcels);
forcePollAndWaitForIdle();
// verify service recorded history
@@ -1478,13 +1497,15 @@
// Build 3G template, type unknown template to get stats while network type is unknown
// and type all template to get the sum of all network type stats.
final NetworkTemplate template3g =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS,
+ METERED_YES);
final NetworkTemplate templateUnknown =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ METERED_YES);
final NetworkTemplate templateAll =
- buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL);
+ buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL, METERED_YES);
final NetworkStateSnapshot[] states =
- new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
+ new NetworkStateSnapshot[]{buildMobileState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1561,7 +1582,7 @@
// Pretend mobile network comes online, but wifi is the default network.
expectDefaultSettings();
NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{
- buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)};
+ buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobileState(IMSI_1)};
expectNetworkStatsUidDetail(buildEmptyStats());
mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states),
new UnderlyingNetworkInfo[0]);
@@ -1580,7 +1601,7 @@
// Verify mobile summary is not changed by the operation count.
final NetworkTemplate templateMobile =
- buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL);
+ buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL, METERED_YES);
final NetworkStats statsMobile = mSession.getSummaryForAllUid(
templateMobile, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertValues(statsMobile, IFACE_ALL, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
@@ -1598,6 +1619,43 @@
DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 2);
}
+ @Test
+ public void testTetheringEventCallback_onUpstreamChanged() throws Exception {
+ // Register custom provider and retrieve callback.
+ final TestableNetworkStatsProviderBinder provider =
+ new TestableNetworkStatsProviderBinder();
+ final INetworkStatsProviderCallback cb =
+ mService.registerNetworkStatsProvider("TEST-TETHERING-OFFLOAD", provider);
+ assertNotNull(cb);
+ provider.assertNoCallback();
+
+ // Post upstream changed event, verify the service will pull for stats.
+ mTetheringEventCallback.onUpstreamChanged(WIFI_NETWORK);
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
+ }
+
+ /**
+ * Verify the service will throw exceptions if the template is location sensitive but
+ * the permission is not granted.
+ */
+ @Test
+ public void testEnforceTemplateLocationPermission() throws Exception {
+ when(mLocationPermissionChecker.checkCallersLocationPermission(
+ any(), any(), anyInt(), anyBoolean(), any())).thenReturn(false);
+ initWifiStats(buildWifiState(true, TEST_IFACE, IMSI_1));
+ assertThrows(SecurityException.class, () ->
+ assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0));
+ // Templates w/o wifi network keys can query stats as usual.
+ assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
+ assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
+
+ when(mLocationPermissionChecker.checkCallersLocationPermission(
+ any(), any(), anyInt(), anyBoolean(), any())).thenReturn(true);
+ assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
+ assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
+ assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
+ }
+
private static File getBaseDir(File statsDir) {
File baseDir = new File(statsDir, "netstats");
baseDir.mkdirs();
@@ -1613,7 +1671,8 @@
private void assertNetworkTotal(NetworkTemplate template, long start, long end, long rxBytes,
long rxPackets, long txBytes, long txPackets, int operations) throws Exception {
// verify history API
- final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELD_ALL);
+ final NetworkStatsHistory history =
+ mSession.getHistoryIntervalForNetwork(template, FIELD_ALL, start, end);
assertValues(history, start, end, rxBytes, rxPackets, txBytes, txPackets, operations);
// verify summary API
@@ -1655,6 +1714,8 @@
return states[0].getLinkProperties().getInterfaceName();
}
+ // TODO: These expect* methods are used to have NetworkStatsService returns the given stats
+ // instead of expecting anything. Therefore, these methods should be renamed properly.
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
expectNetworkStatsSummaryDev(summary.clone());
expectNetworkStatsSummaryXt(summary.clone());
@@ -1669,16 +1730,17 @@
}
private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
- expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0));
+ final TetherStatsParcel[] tetherStatsParcels = {};
+ expectNetworkStatsUidDetail(detail, tetherStatsParcels);
}
- private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats)
- throws Exception {
+ private void expectNetworkStatsUidDetail(NetworkStats detail,
+ TetherStatsParcel[] tetherStatsParcels) throws Exception {
when(mStatsFactory.readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL))
.thenReturn(detail);
// also include tethering details, since they are folded into UID
- when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats);
+ when(mNetd.tetherGetStats()).thenReturn(tetherStatsParcels);
}
private void expectDefaultSettings() throws Exception {
@@ -1740,19 +1802,25 @@
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
- capabilities.setSSID(TEST_SSID);
+ capabilities.setTransportInfo(sWifiInfo);
return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, subscriberId, TYPE_WIFI);
}
- private static NetworkStateSnapshot buildMobile3gState(String subscriberId) {
- return buildMobile3gState(subscriberId, false /* isRoaming */);
+ private static NetworkStateSnapshot buildMobileState(String subscriberId) {
+ return buildMobileState(TEST_IFACE, subscriberId, false /* isTemporarilyNotMetered */,
+ false /* isRoaming */);
}
- private static NetworkStateSnapshot buildMobile3gState(String subscriberId, boolean isRoaming) {
+ private static NetworkStateSnapshot buildMobileState(String iface, String subscriberId,
+ boolean isTemporarilyNotMetered, boolean isRoaming) {
final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(TEST_IFACE);
+ prop.setInterfaceName(iface);
final NetworkCapabilities capabilities = new NetworkCapabilities();
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
+
+ if (isTemporarilyNotMetered) {
+ capabilities.addCapability(
+ NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ }
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
return new NetworkStateSnapshot(
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 2bc385c..43aeec6 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -16,6 +16,9 @@
package com.android.server.net;
+import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
+import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -35,12 +38,12 @@
import android.content.Context;
import android.net.NetworkTemplate;
import android.os.Build;
-import android.os.test.TestLooper;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
+import android.os.Looper;
+import android.os.Parcel;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
+import android.util.SparseArray;
import com.android.internal.util.CollectionUtils;
import com.android.server.net.NetworkStatsSubscriptionsMonitor.RatTypeListener;
@@ -71,26 +74,30 @@
@Mock private Context mContext;
@Mock private SubscriptionManager mSubscriptionManager;
@Mock private TelephonyManager mTelephonyManager;
+ private final SparseArray<TelephonyManager> mTelephonyManagerOfSub = new SparseArray<>();
+ private final SparseArray<RatTypeListener> mRatTypeListenerOfSub = new SparseArray<>();
@Mock private NetworkStatsSubscriptionsMonitor.Delegate mDelegate;
private final List<Integer> mTestSubList = new ArrayList<>();
private final Executor mExecutor = Executors.newSingleThreadExecutor();
private NetworkStatsSubscriptionsMonitor mMonitor;
- private TestLooper mTestLooper = new TestLooper();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+ // TODO(b/213280079): Start a different thread and prepare the looper, create the monitor
+ // on that thread instead of using the test main thread looper.
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
when(mContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
.thenReturn(mSubscriptionManager);
when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE)))
.thenReturn(mTelephonyManager);
- mMonitor = new NetworkStatsSubscriptionsMonitor(mContext, mTestLooper.getLooper(),
- mExecutor, mDelegate);
+ mMonitor = new NetworkStatsSubscriptionsMonitor(mContext, mExecutor, mDelegate);
}
@Test
@@ -116,16 +123,29 @@
return list;
}
- private void setRatTypeForSub(List<RatTypeListener> listeners,
- int subId, int type) {
- final ServiceState serviceState = mock(ServiceState.class);
- when(serviceState.getDataNetworkType()).thenReturn(type);
- final RatTypeListener match = CollectionUtils
- .find(listeners, it -> it.getSubId() == subId);
+ private TelephonyDisplayInfo makeTelephonyDisplayInfo(
+ int networkType, int overrideNetworkType) {
+ // Create from parcel since final classes cannot be mocked and there is no exposed public
+ // constructors.
+ Parcel p = Parcel.obtain();
+ p.writeInt(networkType);
+ p.writeInt(overrideNetworkType);
+
+ p.setDataPosition(0);
+ return TelephonyDisplayInfo.CREATOR.createFromParcel(p);
+ }
+
+ private void setRatTypeForSub(int subId, int type) {
+ setRatTypeForSub(subId, type, OVERRIDE_NETWORK_TYPE_NONE);
+ }
+
+ private void setRatTypeForSub(int subId, int type, int overrideType) {
+ final TelephonyDisplayInfo displayInfo = makeTelephonyDisplayInfo(type, overrideType);
+ final RatTypeListener match = mRatTypeListenerOfSub.get(subId);
if (match == null) {
fail("Could not find listener with subId: " + subId);
}
- match.onServiceStateChanged(serviceState);
+ match.onDisplayInfoChanged(displayInfo);
}
private void addTestSub(int subId, String subscriberId) {
@@ -136,21 +156,47 @@
final int[] subList = convertArrayListToIntArray(mTestSubList);
when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList);
- when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId);
- mMonitor.onSubscriptionsChanged();
+ updateSubscriberIdForTestSub(subId, subscriberId);
}
private void updateSubscriberIdForTestSub(int subId, @Nullable final String subscriberId) {
- when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId);
+ final TelephonyManager telephonyManagerOfSub;
+ if (mTelephonyManagerOfSub.contains(subId)) {
+ telephonyManagerOfSub = mTelephonyManagerOfSub.get(subId);
+ } else {
+ telephonyManagerOfSub = mock(TelephonyManager.class);
+ mTelephonyManagerOfSub.put(subId, telephonyManagerOfSub);
+ }
+ when(telephonyManagerOfSub.getSubscriberId()).thenReturn(subscriberId);
+ when(mTelephonyManager.createForSubscriptionId(subId)).thenReturn(telephonyManagerOfSub);
mMonitor.onSubscriptionsChanged();
}
+ private void assertAndCaptureRatTypeListenerRegistration(int subId) {
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+ verify(mTelephonyManagerOfSub.get(subId))
+ .registerTelephonyCallback(any(), ratTypeListenerCaptor.capture());
+ final RatTypeListener listener = CollectionUtils
+ .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == subId);
+ assertNotNull(listener);
+ mRatTypeListenerOfSub.put(subId, listener);
+ }
+
private void removeTestSub(int subId) {
// Remove subId from TestSubList.
mTestSubList.removeIf(it -> it == subId);
final int[] subList = convertArrayListToIntArray(mTestSubList);
when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList);
mMonitor.onSubscriptionsChanged();
+ assertRatTypeListenerDeregistration(subId);
+ mRatTypeListenerOfSub.delete(subId);
+ mTelephonyManagerOfSub.delete(subId);
+ }
+
+ private void assertRatTypeListenerDeregistration(int subId) {
+ verify(mTelephonyManagerOfSub.get(subId))
+ .unregisterTelephonyCallback(eq(mRatTypeListenerOfSub.get(subId)));
}
private void assertRatTypeChangedForSub(String subscriberId, int ratType) {
@@ -171,9 +217,6 @@
@Test
public void testSubChangedAndRatTypeChanged() {
- final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
- ArgumentCaptor.forClass(RatTypeListener.class);
-
mMonitor.start();
// Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
// before changing RAT type.
@@ -183,15 +226,14 @@
// Insert sim2.
addTestSub(TEST_SUBID2, TEST_IMSI2);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
- verify(mTelephonyManager, times(2)).listen(ratTypeListenerCaptor.capture(),
- eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID2);
reset(mDelegate);
// Set RAT type of sim1 to UMTS.
// Verify RAT type of sim1 after subscription gets onCollapsedRatTypeChanged() callback
// and others remain untouched.
- setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
- TelephonyManager.NETWORK_TYPE_UMTS);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_UMTS);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
@@ -200,8 +242,7 @@
// Set RAT type of sim2 to LTE.
// Verify RAT type of sim2 after subscription gets onCollapsedRatTypeChanged() callback
// and others remain untouched.
- setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID2,
- TelephonyManager.NETWORK_TYPE_LTE);
+ setRatTypeForSub(TEST_SUBID2, TelephonyManager.NETWORK_TYPE_LTE);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_LTE);
assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
@@ -210,7 +251,6 @@
// Remove sim2 and verify that callbacks are fired and RAT type is correct for sim2.
// while the other two remain untouched.
removeTestSub(TEST_SUBID2);
- verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
@@ -218,13 +258,12 @@
// Set RAT type of sim1 to UNKNOWN. Then stop monitoring subscription changes
// and verify that the listener for sim1 is removed.
- setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
- TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
reset(mDelegate);
mMonitor.stop();
- verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeListenerDeregistration(TEST_SUBID1);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
}
@@ -236,104 +275,84 @@
// before changing RAT type. Also capture listener for later use.
addTestSub(TEST_SUBID1, TEST_IMSI1);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
- final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
- ArgumentCaptor.forClass(RatTypeListener.class);
- verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
- eq(PhoneStateListener.LISTEN_SERVICE_STATE));
- final RatTypeListener listener = CollectionUtils
- .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1);
- assertNotNull(listener);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
+ final RatTypeListener listener = mRatTypeListenerOfSub.get(TEST_SUBID1);
// Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs
// NETWORK_TYPE_5G_NSA.
- final ServiceState serviceState = mock(ServiceState.class);
- when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
- when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
- listener.onServiceStateChanged(serviceState);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_LTE,
+ OVERRIDE_NETWORK_TYPE_NR_NSA);
assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA);
reset(mDelegate);
// Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE.
- when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
- listener.onServiceStateChanged(serviceState);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_LTE,
+ OVERRIDE_NETWORK_TYPE_NONE);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
reset(mDelegate);
// Verify NR connected with other RAT type does not take effect.
- when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS);
- when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
- listener.onServiceStateChanged(serviceState);
+ // This should not be happened in practice.
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_UMTS,
+ OVERRIDE_NETWORK_TYPE_NR_NSA);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
reset(mDelegate);
// Set RAT type to 5G standalone mode, the RAT type should be NR.
- setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
- TelephonyManager.NETWORK_TYPE_NR);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_NR);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
reset(mDelegate);
// Set NR state to none in standalone mode does not change anything.
- when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR);
- when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
- listener.onServiceStateChanged(serviceState);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_NR, OVERRIDE_NETWORK_TYPE_NONE);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
}
@Test
public void testSubscriberIdUnavailable() {
- final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
- ArgumentCaptor.forClass(RatTypeListener.class);
-
mMonitor.start();
// Insert sim1, set subscriberId to null which is normal in SIM PIN locked case.
// Verify RAT type is NETWORK_TYPE_UNKNOWN and service will not perform listener
// registration.
addTestSub(TEST_SUBID1, null);
- verify(mTelephonyManager, never()).listen(any(), anyInt());
+ verify(mTelephonyManagerOfSub.get(TEST_SUBID1), never()).listen(any(), anyInt());
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
// Set IMSI for sim1, verify the listener will be registered.
updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
- verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
- eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
reset(mTelephonyManager);
- when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
// Set RAT type of sim1 to UMTS. Verify RAT type of sim1 is changed.
- setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
- TelephonyManager.NETWORK_TYPE_UMTS);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_UMTS);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
reset(mDelegate);
// Set IMSI to null again to simulate somehow IMSI is not available, such as
// modem crash. Verify service should unregister listener.
updateSubscriberIdForTestSub(TEST_SUBID1, null);
- verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
- eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeListenerDeregistration(TEST_SUBID1);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
reset(mDelegate);
- clearInvocations(mTelephonyManager);
+ clearInvocations(mTelephonyManagerOfSub.get(TEST_SUBID1));
// Simulate somehow IMSI is back. Verify service will register with
// another listener and fire callback accordingly.
final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
ArgumentCaptor.forClass(RatTypeListener.class);
updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
- verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
- eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
reset(mDelegate);
- clearInvocations(mTelephonyManager);
+ clearInvocations(mTelephonyManagerOfSub.get(TEST_SUBID1));
// Set RAT type of sim1 to LTE. Verify RAT type of sim1 still works.
- setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
- TelephonyManager.NETWORK_TYPE_LTE);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_LTE);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
reset(mDelegate);
mMonitor.stop();
- verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor2.getValue()),
- eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeListenerDeregistration(TEST_SUBID1);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
}
@@ -349,30 +368,24 @@
// Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
// before changing RAT type.
addTestSub(TEST_SUBID1, TEST_IMSI1);
- final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
- ArgumentCaptor.forClass(RatTypeListener.class);
- verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
- eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
// Set RAT type of sim1 to UMTS.
// Verify RAT type of sim1 changes accordingly.
- setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
- TelephonyManager.NETWORK_TYPE_UMTS);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_UMTS);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
reset(mDelegate);
- clearInvocations(mTelephonyManager);
+ clearInvocations(mTelephonyManagerOfSub.get(TEST_SUBID1));
// Simulate IMSI of sim1 changed to IMSI2. Verify the service will register with
// another listener and remove the old one. The RAT type of new IMSI stays at
// NETWORK_TYPE_UNKNOWN until received initial callback from telephony.
- final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
- ArgumentCaptor.forClass(RatTypeListener.class);
updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2);
- verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
- eq(PhoneStateListener.LISTEN_SERVICE_STATE));
- verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
- eq(PhoneStateListener.LISTEN_NONE));
+ final RatTypeListener oldListener = mRatTypeListenerOfSub.get(TEST_SUBID1);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
+ verify(mTelephonyManagerOfSub.get(TEST_SUBID1), times(1))
+ .unregisterTelephonyCallback(eq(oldListener));
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
reset(mDelegate);
@@ -380,8 +393,7 @@
// Set RAT type of sim1 to UMTS for new listener to simulate the initial callback received
// from telephony after registration. Verify RAT type of sim1 changes with IMSI2
// accordingly.
- setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
- TelephonyManager.NETWORK_TYPE_UMTS);
+ setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_UMTS);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UMTS);
reset(mDelegate);
diff --git a/tests/unit/jni/Android.bp b/tests/unit/jni/Android.bp
index 1c1ba9e..fe971e7 100644
--- a/tests/unit/jni/Android.bp
+++ b/tests/unit/jni/Android.bp
@@ -13,6 +13,8 @@
"-Wthread-safety",
],
+ header_libs: ["bpf_connectivity_headers"],
+
srcs: [
":lib_networkStatsFactory_native",
"test_onload.cpp",
diff --git a/tests/unit/vpn-jarjar-rules.txt b/tests/unit/vpn-jarjar-rules.txt
new file mode 100644
index 0000000..16661b9
--- /dev/null
+++ b/tests/unit/vpn-jarjar-rules.txt
@@ -0,0 +1,4 @@
+# Only keep classes imported by ConnectivityServiceTest
+keep com.android.server.VpnManagerService
+keep com.android.server.connectivity.Vpn
+keep com.android.server.connectivity.VpnProfileStore
\ No newline at end of file