Merge "Add a simple test for ConnectivityService#dump."
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
index 6847c74..bc1d002 100644
--- a/OWNERS_core_networking
+++ b/OWNERS_core_networking
@@ -12,6 +12,7 @@
maze@google.com
nuccachen@google.com
paulhu@google.com
+prohr@google.com
reminv@google.com
satk@google.com
waynema@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/TEST_MAPPING b/TEST_MAPPING
index 90312a4..ffbd1fc 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -23,11 +23,25 @@
},
{
"name": "TetheringIntegrationTests"
+ },
+ {
+ "name": "traffic_controller_unit_test"
}
],
"postsubmit": [
{
"name": "TetheringPrivilegedTests"
+ },
+ // TODO: move to presubmit when known green.
+ {
+ "name": "bpf_existence_test"
+ },
+ {
+ "name": "libclat_test"
+ },
+ {
+ "name": "traffic_controller_unit_test",
+ "keywords": ["netd-device-kernel-4.9", "netd-device-kernel-4.14"]
}
],
"mainline-presubmit": [
@@ -44,6 +58,9 @@
},
{
"name": "ConnectivityCoverageTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
+ },
+ {
+ "name": "traffic_controller_unit_test[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
}
],
"mainline-postsubmit": [
@@ -54,6 +71,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": [
@@ -61,6 +82,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 bb435d8..041a3ae 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -21,6 +21,7 @@
java_defaults {
name: "TetheringApiLevel",
sdk_version: "module_current",
+ target_sdk_version: "31",
min_sdk_version: "30",
}
@@ -109,6 +110,7 @@
static_libs: [
"libnet_utils_device_common_bpfjni",
"libnetjniutils",
+ "libtcutils",
],
// We cannot use plain "libc++" here to link libc++ dynamically because it results in:
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 ee1c5fe..611f100 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -51,14 +51,21 @@
first: {
jni_libs: [
"libservice-connectivity",
- "libcom_android_connectivity_com_android_net_module_util_jni"
+ "libcom_android_connectivity_com_android_net_module_util_jni",
+ "libtraffic_controller_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",
],
@@ -91,6 +98,7 @@
name: "com.android.tethering-bootclasspath-fragment",
contents: [
"framework-connectivity",
+ "framework-connectivity-tiramisu",
"framework-tethering",
],
apex_available: ["com.android.tethering"],
@@ -112,7 +120,13 @@
// 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_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ 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"],
},
}
diff --git a/Tethering/apex/canned_fs_config b/Tethering/apex/canned_fs_config
new file mode 100644
index 0000000..06e9617
--- /dev/null
+++ b/Tethering/apex/canned_fs_config
@@ -0,0 +1,2 @@
+/bin/for-system 0 1000 0550
+/bin/for-system/clatd 1029 1029 06755
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-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/jni/com_android_networkstack_tethering_BpfUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
deleted file mode 100644
index f9e4824..0000000
--- a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
+++ /dev/null
@@ -1,397 +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>
-#include <sys/utsname.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;
-}
-
-// -----------------------------------------------------------------------------
-// TODO - just use BpfUtils.h once that is available in sc-mainline-prod and has kernelVersion()
-//
-// In the mean time copying verbatim from:
-// system/bpf/libbpf_android/include/bpf/BpfUtils.h
-// and
-// system/bpf/libbpf_android/BpfUtils.cpp
-
-#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
-
-static unsigned kernelVersion() {
- struct utsname buf;
- int ret = uname(&buf);
- if (ret) return 0;
-
- unsigned kver_major;
- unsigned kver_minor;
- unsigned kver_sub;
- char discard;
- ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub, &discard);
- // Check the device kernel version
- if (ret < 3) return 0;
-
- return KVER(kver_major, kver_minor, kver_sub);
-}
-
-static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
- return kernelVersion() >= KVER(major, minor, sub);
-}
-// -----------------------------------------------------------------------------
-
-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;
- }
-
- // Backwards compatibility with pre-GKI kernels that use various custom
- // ARPHRD_* for their cellular interface
- switch (rv) {
- // ARPHRD_PUREIP on at least some Mediatek Android kernels
- // example: wembley with 4.19 kernel
- case 520:
- // in Linux 4.14+ rmnet support was upstreamed and ARHRD_RAWIP became 519,
- // but it is 530 on at least some Qualcomm Android 4.9 kernels with rmnet
- // example: Pixel 3 family
- case 530:
- // >5.4 kernels are GKI2.0 and thus upstream compatible, however 5.10
- // shipped with Android S, so (for safety) let's limit ourselves to
- // >5.10, ie. 5.11+ as a guarantee we're on Android T+ and thus no
- // longer need this non-upstream compatibility logic
- static bool is_pre_5_11_kernel = !isAtLeastKernelVersion(5, 11, 0);
- if (is_pre_5_11_kernel) return false;
- }
-
- switch (rv) {
- case ARPHRD_ETHER:
- return true;
- case ARPHRD_NONE:
- case ARPHRD_PPP:
- case ARPHRD_RAWIP:
- 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/onload.cpp b/Tethering/jni/onload.cpp
index 72895f1..ed80128 100644
--- a/Tethering/jni/onload.cpp
+++ b/Tethering/jni/onload.cpp
@@ -23,6 +23,7 @@
namespace android {
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);
@@ -39,9 +40,10 @@
if (register_com_android_net_module_util_BpfMap(env,
"com/android/networkstack/tethering/util/BpfMap") < 0) return JNI_ERR;
- if (register_com_android_networkstack_tethering_BpfCoordinator(env) < 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_BpfUtils(env) < 0) return JNI_ERR;
+ if (register_com_android_networkstack_tethering_BpfCoordinator(env) < 0) return JNI_ERR;
return JNI_VERSION_1_6;
}
diff --git a/Tethering/proguard.flags b/Tethering/proguard.flags
index f62df7f..6735317 100644
--- a/Tethering/proguard.flags
+++ b/Tethering/proguard.flags
@@ -8,6 +8,10 @@
native <methods>;
}
+-keep class com.android.networkstack.tethering.util.TcUtils {
+ native <methods>;
+}
+
-keepclassmembers public class * extends com.android.networkstack.tethering.util.Struct {
*;
}
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index b4228da..2bb19db 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -614,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 */);
}
@@ -651,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);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
index 4f095cf..77efb51 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
@@ -24,6 +24,8 @@
import androidx.annotation.NonNull;
+import com.android.net.module.util.TcUtils;
+
import java.io.IOException;
/**
@@ -53,11 +55,11 @@
static final boolean DOWNSTREAM = true;
static final boolean UPSTREAM = false;
- // The priority of clat/tether hooks - smaller is higher priority.
+ // The priority of tether hooks - smaller is higher priority.
// TC tether is higher priority then TC clat to match XDP winning over TC.
- // Sync from system/netd/server/OffloadUtils.h.
- static final short PRIO_TETHER6 = 1;
- static final short PRIO_TETHER4 = 2;
+ // Sync from system/netd/server/TcUtils.h.
+ static final short PRIO_TETHER6 = 2;
+ static final short PRIO_TETHER4 = 3;
// note that the above must be lower than PRIO_CLAT from netd's OffloadUtils.cpp
private static String makeProgPath(boolean downstream, int ipVersion, boolean ether) {
@@ -82,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);
}
@@ -90,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
@@ -100,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
@@ -121,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);
@@ -129,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/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 55c24d3..db9a64f 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -134,7 +134,12 @@
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;
@@ -265,8 +270,11 @@
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;
@@ -533,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 {
@@ -769,6 +779,9 @@
TETHERING_BLUETOOTH);
}
mPendingPanRequests.clear();
+ mBluetoothIfaceRequest = null;
+ mBluetoothCallback = null;
+ maybeDisableBluetoothIpServing();
});
}
@@ -779,7 +792,11 @@
private void setBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
final boolean enable, final IIntResultListener listener) {
- bluetoothPan.setBluetoothTethering(enable);
+ 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.
@@ -788,6 +805,68 @@
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;
+ }
+
+ 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) {
final EthernetManager em = (EthernetManager) mContext.getSystemService(
Context.ETHERNET_SERVICE);
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/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index a2bd1a5..d2188d1 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -48,7 +48,7 @@
// Use with NetworkStackJarJarRules.
android_library {
name: "TetheringIntegrationTestsLatestSdkLib",
- target_sdk_version: "30",
+ target_sdk_version: "31",
platform_apis: true,
defaults: ["TetheringIntegrationTestsDefaults"],
visibility: [
@@ -128,7 +128,7 @@
name: "TetheringCoverageTests",
platform_apis: true,
min_sdk_version: "30",
- target_sdk_version: "30",
+ target_sdk_version: "31",
test_suites: ["device-tests", "mts-tethering"],
test_config: "AndroidTest_Coverage.xml",
defaults: ["libnetworkstackutilsjni_deps"],
diff --git a/Tethering/tests/mts/Android.bp b/Tethering/tests/mts/Android.bp
index e51d531..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",
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index 5150d39..ecd1a39 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -87,7 +87,7 @@
static_libs: [
"TetheringApiStableLib",
],
- target_sdk_version: "30",
+ target_sdk_version: "31",
visibility: [
"//packages/modules/Connectivity/tests:__subpackages__",
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 2f2cde0..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;
@@ -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/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 40d133a..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,6 +2577,44 @@
@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);
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
+ mLooper.dispatchAll();
+ 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);
@@ -2610,12 +2668,17 @@
mTethering.interfaceAdded(TEST_BT_IFNAME);
mLooper.dispatchAll();
- 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();
+ 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();
@@ -2632,6 +2695,10 @@
}
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),
@@ -2644,19 +2711,30 @@
reset(mNetd);
}
+ 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);
+ reset(mNetd);
}
// 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) {
+ final boolean bindToPanService) throws Exception {
ServiceListener listener = null;
verify(mBluetoothAdapter).isEnabled();
if (bindToPanService) {
@@ -2671,7 +2749,19 @@
verify(mBluetoothAdapter, never()).getProfileProxy(eq(mServiceContext), any(),
anyInt());
}
- verify(mBluetoothPan).setBluetoothTethering(enable);
+
+ 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();
verifyNoMoreInteractions(mBluetoothAdapter, mBluetoothPan);
reset(mBluetoothAdapter, mBluetoothPan);
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index d015ef6..a3ca90c 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -42,7 +42,9 @@
// 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/service/native/libs/libclat",
"//packages/modules/Connectivity/Tethering",
+ "//packages/modules/Connectivity/service/native",
"//packages/modules/Connectivity/tests/unit/jni",
// TODO: remove system/netd/* when all BPF code is moved out of Netd.
"//system/netd/libnetdbpf",
@@ -71,3 +73,29 @@
"-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/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/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 e765ee8..de505c7 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -66,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.
@@ -93,6 +94,7 @@
// 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",
diff --git a/framework/aidl-export/android/net/ProfileNetworkPreference.aidl b/framework/aidl-export/android/net/ProfileNetworkPreference.aidl
new file mode 100644
index 0000000..d7f2402
--- /dev/null
+++ b/framework/aidl-export/android/net/ProfileNetworkPreference.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 ProfileNetworkPreference;
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 827da6d..ce0c868 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -294,7 +294,7 @@
ctor public NetworkCapabilities(android.net.NetworkCapabilities);
method public int describeContents();
method @NonNull public int[] getCapabilities();
- method @NonNull public int[] getEnterpriseCapabilitySubLevels();
+ method @NonNull public int[] getEnterpriseIds();
method public int getLinkDownstreamBandwidthKbps();
method public int getLinkUpstreamBandwidthKbps();
method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
@@ -302,6 +302,7 @@
method public int getSignalStrength();
method @Nullable public android.net.TransportInfo getTransportInfo();
method public boolean hasCapability(int);
+ method public boolean hasEnterpriseId(int);
method public boolean hasTransport(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
@@ -332,6 +333,11 @@
field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10
field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6
field public static final int NET_CAPABILITY_XCAP = 9; // 0x9
+ field public static final int NET_ENTERPRISE_ID_1 = 1; // 0x1
+ field public static final int NET_ENTERPRISE_ID_2 = 2; // 0x2
+ field public static final int NET_ENTERPRISE_ID_3 = 3; // 0x3
+ field public static final int NET_ENTERPRISE_ID_4 = 4; // 0x4
+ field public static final int NET_ENTERPRISE_ID_5 = 5; // 0x5
field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000
field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
field public static final int TRANSPORT_CELLULAR = 0; // 0x0
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 81a1e5d..db08c48 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 {
@@ -121,11 +133,6 @@
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
@@ -149,6 +156,25 @@
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 int getPreferenceEnterpriseId();
+ 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);
+ method @NonNull public android.net.ProfileNetworkPreference.Builder setPreferenceEnterpriseId(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/system-current.txt b/framework/api/system-current.txt
index 8d084e6..b4b3588 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -294,11 +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 addEnterpriseId(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 removeEnterpriseId(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);
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index c21bcfa..7593482 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -16,6 +16,7 @@
package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
import static android.net.NetworkRequest.Type.LISTEN;
import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
@@ -931,6 +932,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
@@ -1078,7 +1121,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
*/
@@ -1086,7 +1130,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.
@@ -1095,13 +1140,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 {
}
/**
@@ -5461,6 +5518,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
@@ -5470,8 +5529,48 @@
@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);
+ if (preference != PROFILE_NETWORK_PREFERENCE_DEFAULT) {
+ preferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ }
+ 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) {
@@ -5489,7 +5588,7 @@
};
}
try {
- mService.setProfileNetworkPreference(profile, preference, proxy);
+ mService.setProfileNetworkPreferences(profile, profileNetworkPreferences, proxy);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -5511,4 +5610,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/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/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 84f7cbb..c98be47 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -16,8 +16,6 @@
package android.net;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import android.annotation.IntDef;
@@ -149,67 +147,83 @@
private String mRequestorPackageName;
/**
- * enterprise capability sub level 1
- * @hide
+ * Enterprise capability identifier 1.
*/
- @SystemApi(client = MODULE_LIBRARIES)
- public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1 = 1;
+ public static final int NET_ENTERPRISE_ID_1 = 1;
/**
- * enterprise capability sub level 2
- * @hide
+ * Enterprise capability identifier 2.
*/
- @SystemApi(client = MODULE_LIBRARIES)
- public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2 = 2;
+ public static final int NET_ENTERPRISE_ID_2 = 2;
/**
- * enterprise capability sub level 3
- * @hide
+ * Enterprise capability identifier 3.
*/
- @SystemApi(client = MODULE_LIBRARIES)
- public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3 = 3;
+ public static final int NET_ENTERPRISE_ID_3 = 3;
/**
- * enterprise capability sub level 4
- * @hide
+ * Enterprise capability identifier 4.
*/
- @SystemApi(client = MODULE_LIBRARIES)
- public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4 = 4;
+ public static final int NET_ENTERPRISE_ID_4 = 4;
/**
- * enterprise capability sub level 5
- * @hide
+ * Enterprise capability identifier 5.
*/
- @SystemApi(client = MODULE_LIBRARIES)
- public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5 = 5;
+ public static final int NET_ENTERPRISE_ID_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,
+ NET_ENTERPRISE_ID_1,
+ NET_ENTERPRISE_ID_2,
+ NET_ENTERPRISE_ID_3,
+ NET_ENTERPRISE_ID_4,
+ NET_ENTERPRISE_ID_5,
})
- public @interface EnterpriseCapabilitySubLevel {
+ public @interface EnterpriseId {
}
/**
- * Bitfield representing the network's enterprise capability sublevel. If any are specified
+ * Bitfield representing the network's enterprise capability identifier. 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
+ * {@see addEnterpriseId} for details on how masks are added
*/
- private int mEnterpriseCapabilitySubLevel;
+ private int mEnterpriseId;
/**
- * @return all the enteprise capabilities sub level set on this {@code NetworkCapability}
- * instance.
+ * Get enteprise identifiers set.
+ *
+ * Get all the enterprise capabilities identifier set on this {@code NetworkCapability}
+ * If NET_CAPABILITY_ENTERPRISE is set and no enterprise ID is set, it is
+ * considered to have NET_CAPABILITY_ENTERPRISE by default.
+ * @return all the enterprise capabilities identifier set.
*
*/
- public @NonNull @EnterpriseCapabilitySubLevel int[] getEnterpriseCapabilitySubLevels() {
- return NetworkCapabilitiesUtils.unpackBits(mEnterpriseCapabilitySubLevel);
+ public @NonNull @EnterpriseId int[] getEnterpriseIds() {
+ if (hasCapability(NET_CAPABILITY_ENTERPRISE) && mEnterpriseId == 0) {
+ return new int[]{NET_ENTERPRISE_ID_1};
+ }
+ return NetworkCapabilitiesUtils.unpackBits(mEnterpriseId);
+ }
+
+ /**
+ * Tests for the presence of an enterprise capability identifier on this instance.
+ *
+ * If NET_CAPABILITY_ENTERPRISE is set and no enterprise ID is set, it is
+ * considered to have NET_CAPABILITY_ENTERPRISE by default.
+ * @param enterpriseId the enterprise capability identifier to be tested for.
+ * @return {@code true} if set on this instance.
+ */
+ public boolean hasEnterpriseId(
+ @EnterpriseId int enterpriseId) {
+ if (enterpriseId == NET_ENTERPRISE_ID_1) {
+ if (hasCapability(NET_CAPABILITY_ENTERPRISE) && mEnterpriseId == 0) {
+ return true;
+ }
+ }
+ return isValidEnterpriseId(enterpriseId)
+ && ((mEnterpriseId & (1L << enterpriseId)) != 0);
}
public NetworkCapabilities() {
@@ -258,7 +272,7 @@
mRequestorPackageName = null;
mSubIds = new ArraySet<>();
mUnderlyingNetworks = null;
- mEnterpriseCapabilitySubLevel = 0;
+ mEnterpriseId = 0;
}
/**
@@ -291,7 +305,7 @@
// mUnderlyingNetworks is an unmodifiable list if non-null, so a defensive copy is not
// necessary.
mUnderlyingNetworks = nc.mUnderlyingNetworks;
- mEnterpriseCapabilitySubLevel = nc.mEnterpriseCapabilitySubLevel;
+ mEnterpriseId = nc.mEnterpriseId;
}
/**
@@ -784,34 +798,34 @@
}
/**
- * 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
+ * Adds the given enterprise capability identifier to this {@code NetworkCapability} instance.
+ * Note that when searching for a network to satisfy a request, all capabilities identifier
* requested must be satisfied.
*
- * @param enterpriseCapabilitySubLevel the enterprise capability sub level to be added.
+ * @param enterpriseId the enterprise capability identifier to be added.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- private @NonNull NetworkCapabilities addEnterpriseCapabilitySubLevel(
- @EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
- checkValidEnterpriseCapabilitySublevel(enterpriseCapabilitySubLevel);
- mEnterpriseCapabilitySubLevel |= 1 << enterpriseCapabilitySubLevel;
+ public @NonNull NetworkCapabilities addEnterpriseId(
+ @EnterpriseId int enterpriseId) {
+ checkValidEnterpriseId(enterpriseId);
+ mEnterpriseId |= 1 << enterpriseId;
return this;
}
/**
- * Removes (if found) the given enterprise capability sublevel from this
- * {@code NetworkCapability} instance that were added via addEnterpriseCapabilitySubLevel(int)
+ * Removes (if found) the given enterprise capability identifier from this
+ * {@code NetworkCapability} instance that were added via addEnterpriseId(int)
*
- * @param enterpriseCapabilitySubLevel the enterprise capability sublevel to be removed.
+ * @param enterpriseId the enterprise capability identifier 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;
+ private @NonNull NetworkCapabilities removeEnterpriseId(
+ @EnterpriseId int enterpriseId) {
+ checkValidEnterpriseId(enterpriseId);
+ final int mask = ~(1 << enterpriseId);
+ mEnterpriseId &= mask;
return this;
}
@@ -915,16 +929,19 @@
return null;
}
- private boolean equalsEnterpriseCapabilitiesSubLevel(@NonNull NetworkCapabilities nc) {
- return nc.mEnterpriseCapabilitySubLevel == this.mEnterpriseCapabilitySubLevel;
+ private boolean equalsEnterpriseCapabilitiesId(@NonNull NetworkCapabilities nc) {
+ return nc.mEnterpriseId == this.mEnterpriseId;
}
- private boolean satisfiedByEnterpriseCapabilitiesSubLevel(@NonNull NetworkCapabilities nc) {
- final int requestedEnterpriseCapabilitiesSubLevel = mEnterpriseCapabilitySubLevel;
- final int providedEnterpriseCapabailitiesSubLevel = nc.mEnterpriseCapabilitySubLevel;
+ private boolean satisfiedByEnterpriseCapabilitiesId(@NonNull NetworkCapabilities nc) {
+ final int requestedEnterpriseCapabilitiesId = mEnterpriseId;
+ final int providedEnterpriseCapabailitiesId = nc.mEnterpriseId;
- if ((providedEnterpriseCapabailitiesSubLevel & requestedEnterpriseCapabilitiesSubLevel)
- == requestedEnterpriseCapabilitiesSubLevel) {
+ if ((providedEnterpriseCapabailitiesId & requestedEnterpriseCapabilitiesId)
+ == requestedEnterpriseCapabilitiesId) {
+ return true;
+ } else if (providedEnterpriseCapabailitiesId == 0
+ && (requestedEnterpriseCapabilitiesId == (1L << NET_ENTERPRISE_ID_1))) {
return true;
} else {
return false;
@@ -1836,7 +1853,7 @@
&& satisfiedByTransportTypes(nc)
&& (onlyImmutable || satisfiedByLinkBandwidths(nc))
&& satisfiedBySpecifier(nc)
- && satisfiedByEnterpriseCapabilitiesSubLevel(nc)
+ && satisfiedByEnterpriseCapabilitiesId(nc)
&& (onlyImmutable || satisfiedBySignalStrength(nc))
&& (onlyImmutable || satisfiedByUids(nc))
&& (onlyImmutable || satisfiedBySSID(nc))
@@ -1940,7 +1957,7 @@
&& equalsAdministratorUids(that)
&& equalsSubscriptionIds(that)
&& equalsUnderlyingNetworks(that)
- && equalsEnterpriseCapabilitiesSubLevel(that);
+ && equalsEnterpriseCapabilitiesId(that);
}
@Override
@@ -1965,7 +1982,7 @@
+ Arrays.hashCode(mAdministratorUids) * 61
+ Objects.hashCode(mSubIds) * 67
+ Objects.hashCode(mUnderlyingNetworks) * 71
- + mEnterpriseCapabilitySubLevel * 73;
+ + mEnterpriseId * 73;
}
@Override
@@ -2001,7 +2018,7 @@
dest.writeString(mRequestorPackageName);
dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
dest.writeTypedList(mUnderlyingNetworks);
- dest.writeInt(mEnterpriseCapabilitySubLevel);
+ dest.writeInt(mEnterpriseId);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -2031,7 +2048,7 @@
netCap.mSubIds.add(subIdInts[i]);
}
netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR));
- netCap.mEnterpriseCapabilitySubLevel = in.readInt();
+ netCap.mEnterpriseId = in.readInt();
return netCap;
}
@Override
@@ -2123,10 +2140,10 @@
sb.append(" SubscriptionIds: ").append(mSubIds);
}
- if (0 != mEnterpriseCapabilitySubLevel) {
- sb.append(" EnterpriseCapabilitySublevel: ");
- appendStringRepresentationOfBitMaskToStringBuilder(sb, mEnterpriseCapabilitySubLevel,
- NetworkCapabilities::enterpriseCapabilitySublevelNameOf, "&");
+ if (0 != mEnterpriseId) {
+ sb.append(" EnterpriseId: ");
+ appendStringRepresentationOfBitMaskToStringBuilder(sb, mEnterpriseId,
+ NetworkCapabilities::enterpriseIdNameOf, "&");
}
sb.append(" UnderlyingNetworks: ");
@@ -2228,7 +2245,7 @@
}
}
- private static @NonNull String enterpriseCapabilitySublevelNameOf(
+ private static @NonNull String enterpriseIdNameOf(
@NetCapability int capability) {
return Integer.toString(capability);
}
@@ -2273,17 +2290,17 @@
}
}
- 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 boolean isValidEnterpriseId(
+ @NetworkCapabilities.EnterpriseId int enterpriseId) {
+ return enterpriseId >= NET_ENTERPRISE_ID_1
+ && enterpriseId <= NET_ENTERPRISE_ID_5;
}
- private static void checkValidEnterpriseCapabilitySublevel(
- @NetworkCapabilities.EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
- if (!isValidEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel)) {
- throw new IllegalArgumentException("enterprise capability sublevel "
- + enterpriseCapabilitySubLevel + " is out of range");
+ private static void checkValidEnterpriseId(
+ @NetworkCapabilities.EnterpriseId int enterpriseId) {
+ if (!isValidEnterpriseId(enterpriseId)) {
+ throw new IllegalArgumentException("enterprise capability identifier "
+ + enterpriseId + " is out of range");
}
}
@@ -2613,33 +2630,33 @@
}
/**
- * 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
+ * Adds the given enterprise capability identifier.
+ * Note that when searching for a network to satisfy a request, all capabilities identifier
+ * requested must be satisfied. Enterprise capability identifier is applicable only
* for NET_CAPABILITY_ENTERPRISE capability
*
- * @param enterpriseCapabilitySubLevel enterprise capability sub-level.
+ * @param enterpriseId enterprise capability identifier.
*
* @return this builder
*/
@NonNull
- public Builder addEnterpriseCapabilitySubLevel(
- @EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
- mCaps.addEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel);
+ public Builder addEnterpriseId(
+ @EnterpriseId int enterpriseId) {
+ mCaps.addEnterpriseId(enterpriseId);
return this;
}
/**
- * Removes the given enterprise capability sub level. Enterprise capability sub-level is
+ * Removes the given enterprise capability identifier. Enterprise capability identifier is
* applicable only for NET_CAPABILITY_ENTERPRISE capability
*
- * @param enterpriseCapabilitySubLevel the enterprise capability subLevel
+ * @param enterpriseId the enterprise capability identifier
* @return this builder
*/
@NonNull
- public Builder removeEnterpriseCapabilitySubLevel(
- @EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
- mCaps.removeEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel);
+ public Builder removeEnterpriseId(
+ @EnterpriseId int enterpriseId) {
+ mCaps.removeEnterpriseId(enterpriseId);
return this;
}
@@ -2902,10 +2919,10 @@
}
}
- if ((mCaps.getEnterpriseCapabilitySubLevels().length != 0)
+ if ((mCaps.getEnterpriseIds().length != 0)
&& !mCaps.hasCapability(NET_CAPABILITY_ENTERPRISE)) {
- throw new IllegalStateException("Enterprise capability sublevel is applicable only"
- + " with ENTERPRISE capability.");
+ throw new IllegalStateException("Enterprise capability identifier is applicable"
+ + " only with ENTERPRISE capability.");
}
return new NetworkCapabilities(mCaps);
}
diff --git a/framework/src/android/net/ProfileNetworkPreference.java b/framework/src/android/net/ProfileNetworkPreference.java
new file mode 100644
index 0000000..f43acce
--- /dev/null
+++ b/framework/src/android/net/ProfileNetworkPreference.java
@@ -0,0 +1,307 @@
+/*
+ * 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 static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_5;
+
+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 @NetworkCapabilities.EnterpriseId int mPreferenceEnterpriseId;
+ private final List<Integer> mIncludedUids;
+ private final List<Integer> mExcludedUids;
+
+ private ProfileNetworkPreference(int preference, List<Integer> includedUids,
+ List<Integer> excludedUids,
+ @NetworkCapabilities.EnterpriseId int preferenceEnterpriseId) {
+ mPreference = preference;
+ mPreferenceEnterpriseId = preferenceEnterpriseId;
+ 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());
+ mPreferenceEnterpriseId = in.readInt();
+ }
+
+ 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);
+ }
+
+ /**
+ * Get preference enterprise identifier.
+ *
+ * Preference enterprise identifier will be used to create different network preferences
+ * within enterprise preference category.
+ * Valid values starts from PROFILE_NETWORK_PREFERENCE_ENTERPRISE_ID_1 to
+ * NetworkCapabilities.NET_ENTERPRISE_ID_5.
+ * Preference identifier is not applicable if preference is set as
+ * PROFILE_NETWORK_PREFERENCE_DEFAULT. Default value is
+ * NetworkCapabilities.NET_ENTERPRISE_ID_1.
+ * @return Preference enterprise identifier.
+ *
+ */
+ public @NetworkCapabilities.EnterpriseId int getPreferenceEnterpriseId() {
+ return mPreferenceEnterpriseId;
+ }
+
+ @Override
+ public String toString() {
+ return "ProfileNetworkPreference{"
+ + "mPreference=" + getPreference()
+ + "mIncludedUids=" + mIncludedUids.toString()
+ + "mExcludedUids=" + mExcludedUids.toString()
+ + "mPreferenceEnterpriseId=" + mPreferenceEnterpriseId
+ + '}';
+ }
+
+ @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))
+ && mPreferenceEnterpriseId == that.mPreferenceEnterpriseId;
+ }
+
+ @Override
+ public int hashCode() {
+ return mPreference
+ + mPreferenceEnterpriseId * 2
+ + (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<>();
+ private int mPreferenceEnterpriseId;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Check if given preference enterprise identifier is valid
+ *
+ * Valid values starts from PROFILE_NETWORK_PREFERENCE_ENTERPRISE_ID_1 to
+ * NetworkCapabilities.NET_ENTERPRISE_ID_5.
+ * @return True if valid else false
+ * @hide
+ */
+ private boolean isEnterpriseIdentifierValid(
+ @NetworkCapabilities.EnterpriseId int identifier) {
+ if ((identifier >= NET_ENTERPRISE_ID_1)
+ && (identifier <= NET_ENTERPRISE_ID_5)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 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");
+ }
+
+ if (((mPreference != PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ && (!isEnterpriseIdentifierValid(mPreferenceEnterpriseId)))
+ || ((mPreference == PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ && (mPreferenceEnterpriseId != 0))) {
+ throw new IllegalStateException("Invalid preference enterprise identifier");
+ }
+ return new ProfileNetworkPreference(mPreference, mIncludedUids,
+ mExcludedUids, mPreferenceEnterpriseId);
+ }
+
+ /**
+ * Set the preference enterprise identifier.
+ *
+ * Preference enterprise identifier will be used to create different network preferences
+ * within enterprise preference category.
+ * Valid values starts from NetworkCapabilities.NET_ENTERPRISE_ID_1 to
+ * NetworkCapabilities.NET_ENTERPRISE_ID_5.
+ * Preference identifier is not applicable if preference is set as
+ * PROFILE_NETWORK_PREFERENCE_DEFAULT. Default value is
+ * NetworkCapabilities.NET_ENTERPRISE_ID_1.
+ * @param preferenceId preference sub level
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setPreferenceEnterpriseId(
+ @NetworkCapabilities.EnterpriseId int preferenceId) {
+ mPreferenceEnterpriseId = preferenceId;
+ return this;
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ dest.writeInt(mPreference);
+ dest.writeList(mIncludedUids);
+ dest.writeList(mExcludedUids);
+ dest.writeInt(mPreferenceEnterpriseId);
+ }
+
+ @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/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 b1a56ae..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,12 +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");
mConnectivity = new ConnectivityService(context);
+ mNsdService = createNsdService(context);
}
@Override
@@ -39,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 1ec7daa..d1a9004 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -57,12 +57,19 @@
],
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: [
+ "libbase",
+ "libclat",
+ "libip_checksum",
+ "libnetjniutils",
+ ],
shared_libs: [
"liblog",
"libnativehelper",
@@ -108,6 +115,10 @@
"com.android.tethering",
],
lint: { strict_updatability_linting: true },
+ visibility: [
+ "//packages/modules/Connectivity/service-t",
+ "//packages/modules/Connectivity/tests:__subpackages__",
+ ],
}
java_library {
@@ -132,8 +143,13 @@
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: [
@@ -147,3 +163,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 550ab8a..086c6e3 100644
--- a/service/ServiceConnectivityResources/res/values-af/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-af/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Stelselkonnektiwiteithulpbronne"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Meld aan by Wi-Fi-netwerk"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Meld by netwerk aan"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het geen internettoegang nie"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tik vir opsies"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Selnetwerk het nie internettoegang nie"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Netwerk het nie internettoegang nie"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het beperkte konnektiwiteit"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tik om in elk geval te koppel"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Het oorgeskakel na <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobiele data"</item>
- <item msgid="6341719431034774569">"Wi-fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"\'n onbekende netwerktipe"</string>
+ <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 7f1a9db..886b353 100644
--- a/service/ServiceConnectivityResources/res/values-am/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-am/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"የስርዓት ግንኙነት መርጃዎች"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ወደ Wi-Fi አውታረ መረብ በመለያ ግባ"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ወደ አውታረ መረብ በመለያ ይግቡ"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ምንም የበይነ መረብ መዳረሻ የለም"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ለአማራጮች መታ ያድርጉ"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"የተንቀሳቃሽ ስልክ አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> የተገደበ ግንኙነት አለው"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ለማንኛውም ለማገናኘት መታ ያድርጉ"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"ወደ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ተቀይሯል"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"ከ<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ወደ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ተቀይሯል"</string>
+ <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="5454013645032700715">"የተንቀሳቃሽ ስልክ ውሂብ"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"ብሉቱዝ"</item>
- <item msgid="1160736166977503463">"ኢተርኔት"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"አንድ ያልታወቀ አውታረ መረብ ዓይነት"</string>
+ <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 b7a62c5..07d9c2e 100644
--- a/service/ServiceConnectivityResources/res/values-ar/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ar/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"مصادر إمكانية اتصال الخادم"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"تسجيل الدخول إلى شبكة Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"تسجيل الدخول إلى الشبكة"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"لا يتوفّر في <xliff:g id="NETWORK_SSID">%1$s</xliff:g> إمكانية الاتصال بالإنترنت."</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"انقر للحصول على الخيارات."</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"شبكة الجوّال هذه غير متصلة بالإنترنت"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"الشبكة غير متصلة بالإنترنت"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"إمكانية اتصال <xliff:g id="NETWORK_SSID">%1$s</xliff:g> محدودة."</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"يمكنك النقر للاتصال على أي حال."</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"تم التبديل إلى <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"يستخدم الجهاز <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="8831325515040986641">"تم التبديل من <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> إلى <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"بيانات الجوّال"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"بلوتوث"</item>
- <item msgid="1160736166977503463">"إيثرنت"</item>
- <item msgid="7347618872551558605">"شبكة افتراضية خاصة (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="7826330274368951740">"نوع شبكة غير معروف"</string>
+ <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 cf8e6ac..e753cb3 100644
--- a/service/ServiceConnectivityResources/res/values-as/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-as/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ছিষ্টেম সংযোগৰ উৎস"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"অধিক বিকল্পৰ বাবে টিপক"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"ম’বাইল নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ সকলো সেৱাৰ এক্সেছ নাই"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"যিকোনো প্ৰকাৰে সংযোগ কৰিবলৈ টিপক"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ’ল"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"যেতিয়া <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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ’ল"</string>
+ <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="5454013645032700715">"ম’বাইল ডেটা"</item>
- <item msgid="6341719431034774569">"ৱাই-ফাই"</item>
- <item msgid="5081440868800877512">"ব্লুটুথ"</item>
- <item msgid="1160736166977503463">"ইথাৰনেট"</item>
- <item msgid="7347618872551558605">"ভিপিএন"</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="7826330274368951740">"অজ্ঞাত প্ৰকাৰৰ নেটৱৰ্ক"</string>
+ <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 7e927ed..f33a3e3 100644
--- a/service/ServiceConnectivityResources/res/values-az/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-az/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sistem Bağlantı Resursları"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi şəbəkəsinə daxil ol"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Şəbəkəyə daxil olun"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> üçün internet girişi əlçatan deyil"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Seçimlər üçün tıklayın"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobil şəbəkənin internetə girişi yoxdur"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Şəbəkənin internetə girişi yoxdur"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Özəl DNS serverinə giriş mümkün deyil"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> bağlantını məhdudlaşdırdı"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"İstənilən halda klikləyin"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> şəbəkə növünə keçirildi"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<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="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="5454013645032700715">"mobil data"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"naməlum şəbəkə növü"</string>
+ <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 3f1b976..7398e7c 100644
--- a/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resursi za povezivanje sa sistemom"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prijavljivanje na WiFi mrežu"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Prijavite se na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Pristup privatnom DNS serveru nije uspeo"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu vezu"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Dodirnite da biste se ipak povezali"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobilni podaci"</item>
- <item msgid="6341719431034774569">"WiFi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Eternet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"nepoznat tip mreže"</string>
+ <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 21edf24..3459cc7 100644
--- a/service/ServiceConnectivityResources/res/values-be/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-be/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Рэсурсы для падключэння да сістэмы"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Уваход у сетку Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Увайдзіце ў сетку"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> не мае доступу ў інтэрнэт"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Дакраніцеся, каб убачыць параметры"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мабільная сетка не мае доступу ў інтэрнэт"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Сетка не мае доступу ў інтэрнэт"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> мае абмежаваную магчымасць падключэння"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Націсніце, каб падключыцца"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Выкананы пераход да <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Прылада выкарыстоўвае сетку <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="8831325515040986641">"Выкананы пераход з <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> да <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"мабільная перадача даных"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"невядомы тып сеткі"</string>
+ <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 c3c2d72..b4ae618 100644
--- a/service/ServiceConnectivityResources/res/values-bg/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-bg/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ресурси за свързаността на системата"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Влизане в Wi-Fi мрежа"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Вход в мрежата"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> няма достъп до интернет"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Докоснете за опции"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мобилната мрежа няма достъп до интернет"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Мрежата няма достъп до интернет"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Не може да се осъществи достъп до частния DNS сървър"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена свързаност"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Докоснете, за да се свържете въпреки това"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Превключи се към <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Устройството използва <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="8831325515040986641">"Превключи се от <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> към <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"мобилни данни"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"неизвестен тип мрежа"</string>
+ <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 0f693bd..3b32973 100644
--- a/service/ServiceConnectivityResources/res/values-bn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-bn/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"সিস্টেম কানেক্টিভিটি রিসোর্সেস"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ওয়াই-ফাই নেটওয়ার্কে সাইন-ইন করুন"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"নেটওয়ার্কে সাইন-ইন করুন"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর ইন্টারনেটে অ্যাক্সেস নেই"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"মোবাইল নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর সীমিত কানেক্টিভিটি আছে"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"তবুও কানেক্ট করতে ট্যাপ করুন"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string>
+ <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="5454013645032700715">"মোবাইল ডেটা"</item>
- <item msgid="6341719431034774569">"ওয়াই-ফাই"</item>
- <item msgid="5081440868800877512">"ব্লুটুথ"</item>
- <item msgid="1160736166977503463">"ইথারনেট"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"এই নেটওয়ার্কের ধরন অজানা"</string>
+ <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 33d6ed9..0bc0a7c 100644
--- a/service/ServiceConnectivityResources/res/values-bs/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-bs/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Izvori povezivosti sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prijavljivanje na WiFi mrežu"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Prijava na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Nije moguće pristupiti privatnom DNS serveru"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Dodirnite da se ipak povežete"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Prebačeno na: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"prijenos podataka na mobilnoj mreži"</item>
- <item msgid="6341719431034774569">"WiFi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"nepoznata vrsta mreže"</string>
+ <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 04f6bd2..22b9dbd 100644
--- a/service/ServiceConnectivityResources/res/values-ca/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ca/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de connectivitat del sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Inicia la sessió a la xarxa Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Inicia la sessió a la xarxa"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no té accés a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toca per veure les opcions"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"La xarxa mòbil no té accés a Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"La xarxa no té accés a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"No es pot accedir al servidor DNS privat"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> té una connectivitat limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toca per connectar igualment"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Actualment en ús: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"dades mòbils"</item>
- <item msgid="6341719431034774569">"Wi‑Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"un tipus de xarxa desconegut"</string>
+ <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 6309e78..ccf21ee 100644
--- a/service/ServiceConnectivityResources/res/values-cs/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-cs/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Zdroje pro připojení systému"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Přihlásit se k síti Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Přihlásit se k síti"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá přístup k internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Klepnutím zobrazíte možnosti"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilní síť nemá přístup k internetu"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Síť nemá přístup k internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Nelze získat přístup k soukromému serveru DNS"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> umožňuje jen omezené připojení"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Klepnutím se i přesto připojíte"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Přechod na síť <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobilní data"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"neznámý typ sítě"</string>
+ <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 57c58af..a33143e 100644
--- a/service/ServiceConnectivityResources/res/values-da/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-da/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Systemets forbindelsesressourcer"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Log ind på Wi-Fi-netværk"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Log ind på netværk"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetforbindelse"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tryk for at se valgmuligheder"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilnetværket har ingen internetadgang"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Netværket har ingen internetadgang"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Der er ikke adgang til den private DNS-server"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrænset forbindelse"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tryk for at oprette forbindelse alligevel"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Der blev skiftet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobildata"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"en ukendt netværkstype"</string>
+ <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 d0c2551..96cc7d2 100644
--- a/service/ServiceConnectivityResources/res/values-de/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-de/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Systemverbindungsressourcen"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"In WLAN anmelden"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Im Netzwerk anmelden"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> hat keinen Internetzugriff"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Für Optionen tippen"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiles Netzwerk hat keinen Internetzugriff"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Netzwerk hat keinen Internetzugriff"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Schlechte Verbindung mit <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tippen, um die Verbindung trotzdem herzustellen"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Zu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> gewechselt"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Von \"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>\" zu \"<xliff:g id="NEW_NETWORK">%2$s</xliff:g>\" gewechselt"</string>
+ <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="5454013645032700715">"Mobile Daten"</item>
- <item msgid="6341719431034774569">"WLAN"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ein unbekannter Netzwerktyp"</string>
+ <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 1c2838d..b5f319d 100644
--- a/service/ServiceConnectivityResources/res/values-el/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-el/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Πόροι συνδεσιμότητας συστήματος"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Συνδεθείτε στο δίκτυο Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Σύνδεση στο δίκτυο"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Η εφαρμογή <xliff:g id="NETWORK_SSID">%1$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Πατήστε για να δείτε τις επιλογές"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Το δίκτυο κινητής τηλεφωνίας δεν έχει πρόσβαση στο διαδίκτυο."</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Το δίκτυο δεν έχει πρόσβαση στο διαδίκτυο."</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Το δίκτυο <xliff:g id="NETWORK_SSID">%1$s</xliff:g> έχει περιορισμένη συνδεσιμότητα"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Πατήστε για σύνδεση ούτως ή άλλως"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Μετάβαση σε δίκτυο <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Η συσκευή χρησιμοποιεί το δίκτυο <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="8831325515040986641">"Μετάβαση από το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> στο δίκτυο <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"δεδομένα κινητής τηλεφωνίας"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"άγνωστος τύπος δικτύου"</string>
+ <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 db5ad70..c490cf8 100644
--- a/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System connectivity resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"mobile data"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"an unknown network type"</string>
+ <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 db5ad70..c490cf8 100644
--- a/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System connectivity resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"mobile data"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"an unknown network type"</string>
+ <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 db5ad70..c490cf8 100644
--- a/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System connectivity resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"mobile data"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"an unknown network type"</string>
+ <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 db5ad70..c490cf8 100644
--- a/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System connectivity resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"mobile data"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"an unknown network type"</string>
+ <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 2602bfa..67c3659 100644
--- a/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no internet access"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"mobile data"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"an unknown network type"</string>
+ <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 e5f1833..fdca468 100644
--- a/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividad del sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Accede a una red Wi-Fi."</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Acceder a la red"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>no tiene acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Presiona para ver opciones"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"La red móvil no tiene acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"La red no tiene acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"No se puede acceder al servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene conectividad limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Presiona para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Se cambió a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"Datos móviles"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"un tipo de red desconocido"</string>
+ <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 e4f4307..f4a7e3d 100644
--- a/service/ServiceConnectivityResources/res/values-es/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-es/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividad del sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Iniciar sesión en red Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Iniciar sesión en la red"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no tiene acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toca para ver opciones"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"La red móvil no tiene acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"La red no tiene acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"No se ha podido acceder al servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene una conectividad limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toca para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Se ha cambiado a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"datos móviles"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"un tipo de red desconocido"</string>
+ <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 cec408f..cf997b3 100644
--- a/service/ServiceConnectivityResources/res/values-et/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-et/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Süsteemi ühenduvuse allikad"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Logi sisse WiFi-võrku"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Võrku sisselogimine"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Võrgul <xliff:g id="NETWORK_SSID">%1$s</xliff:g> puudub Interneti-ühendus"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Puudutage valikute nägemiseks"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiilsidevõrgul puudub Interneti-ühendus"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Võrgul puudub Interneti-ühendus"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Privaatsele DNS-serverile ei pääse juurde"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Võrgu <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ühendus on piiratud"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Puudutage, kui soovite siiski ühenduse luua"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Lülitati võrgule <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobiilne andmeside"</item>
- <item msgid="6341719431034774569">"WiFi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"tundmatu võrgutüüp"</string>
+ <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 f3ee9b1..2c4e431 100644
--- a/service/ServiceConnectivityResources/res/values-eu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-eu/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sistemaren konexio-baliabideak"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Hasi saioa Wi-Fi sarean"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Hasi saioa sarean"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Ezin da konektatu Internetera <xliff:g id="NETWORK_SSID">%1$s</xliff:g> sarearen bidez"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Sakatu aukerak ikusteko"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Sare mugikorra ezin da konektatu Internetera"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Sarea ezin da konektatu Internetera"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Ezin da atzitu DNS zerbitzari pribatua"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sareak konektagarritasun murriztua du"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Sakatu hala ere konektatzeko"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<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="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="5454013645032700715">"datu-konexioa"</item>
- <item msgid="6341719431034774569">"Wifia"</item>
- <item msgid="5081440868800877512">"Bluetooth-a"</item>
- <item msgid="1160736166977503463">"Ethernet-a"</item>
- <item msgid="7347618872551558605">"VPNa"</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="7826330274368951740">"sare mota ezezaguna"</string>
+ <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 0c5b147..296ce8e 100644
--- a/service/ServiceConnectivityResources/res/values-fa/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-fa/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"منابع اتصال سیستم"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ورود به شبکه Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ورود به سیستم شبکه"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> به اینترنت دسترسی ندارد"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"برای گزینهها ضربه بزنید"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"شبکه تلفن همراه به اینترنت دسترسی ندارد"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"شبکه به اینترنت دسترسی ندارد"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"سرور DNS خصوصی قابل دسترسی نیست"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> اتصال محدودی دارد"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"بههرصورت، برای اتصال ضربه بزنید"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"به <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> تغییر کرد"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"وقتی <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="8831325515040986641">"از <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> به <xliff:g id="NEW_NETWORK">%2$s</xliff:g> تغییر کرد"</string>
+ <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="5454013645032700715">"داده تلفن همراه"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"بلوتوث"</item>
- <item msgid="1160736166977503463">"اترنت"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"نوع شبکه نامشخص"</string>
+ <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 84c0034..07d2907 100644
--- a/service/ServiceConnectivityResources/res/values-fi/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-fi/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Järjestelmän yhteysresurssit"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Kirjaudu Wi-Fi-verkkoon"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Kirjaudu verkkoon"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ei ole yhteydessä internetiin"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Näytä vaihtoehdot napauttamalla."</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiiliverkko ei ole yhteydessä internetiin"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Verkko ei ole yhteydessä internetiin"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> toimii rajoitetulla yhteydellä"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Yhdistä napauttamalla"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> otettiin käyttöön"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<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="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="5454013645032700715">"mobiilidata"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"tuntematon verkon tyyppi"</string>
+ <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 0badf1b..7d5b366 100644
--- a/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ressources de connectivité système"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Connectez-vous au réseau Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Connectez-vous au réseau"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"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="1229067002306296104">"Touchez pour afficher les options"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Le réseau cellulaire n\'offre aucun accès à Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Le réseau n\'offre aucun accès à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Impossible d\'accéder au serveur DNS privé"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"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="6975752539442533034">"Touchez pour vous connecter quand même"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Passé au réseau <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"données cellulaires"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"un type de réseau inconnu"</string>
+ <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 b483525..2331d9b 100644
--- a/service/ServiceConnectivityResources/res/values-fr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-fr/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ressources de connectivité système"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Connectez-vous au réseau Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Se connecter au réseau"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Aucune connexion à Internet pour <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Appuyez ici pour afficher des options."</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Le réseau mobile ne dispose d\'aucun accès à Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Le réseau ne dispose d\'aucun accès à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Impossible d\'accéder au serveur DNS privé"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"La connectivité de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> est limitée"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Appuyer pour se connecter quand même"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Nouveau réseau : <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"données mobiles"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"type de réseau inconnu"</string>
+ <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 dfe8137..f46f84b 100644
--- a/service/ServiceConnectivityResources/res/values-gl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-gl/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividade do sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Inicia sesión na rede wifi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Inicia sesión na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ten acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toca para ver opcións."</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"A rede de telefonía móbil non ten acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"A rede non ten acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Non se puido acceder ao servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"A conectividade de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> é limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toca para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Cambiouse a: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Cambiouse de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"datos móbiles"</item>
- <item msgid="6341719431034774569">"wifi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"un tipo de rede descoñecido"</string>
+ <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 e49b11d..ec9ecd3 100644
--- a/service/ServiceConnectivityResources/res/values-gu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-gu/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"સિસ્ટમની કનેક્ટિવિટીનાં સાધનો"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"વાઇ-ફાઇ નેટવર્ક પર સાઇન ઇન કરો"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"નેટવર્ક પર સાઇન ઇન કરો"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"વિકલ્પો માટે ટૅપ કરો"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"મોબાઇલ નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> મર્યાદિત કનેક્ટિવિટી ધરાવે છે"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"છતાં કનેક્ટ કરવા માટે ટૅપ કરો"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> પર સ્વિચ કર્યું"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"જ્યારે <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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> પરથી <xliff:g id="NEW_NETWORK">%2$s</xliff:g> પર સ્વિચ કર્યું"</string>
+ <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="5454013645032700715">"મોબાઇલ ડેટા"</item>
- <item msgid="6341719431034774569">"વાઇ-ફાઇ"</item>
- <item msgid="5081440868800877512">"બ્લૂટૂથ"</item>
- <item msgid="1160736166977503463">"ઇથરનેટ"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"કોઈ અજાણ્યો નેટવર્કનો પ્રકાર"</string>
+ <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 80ed699..6e3bc6b 100644
--- a/service/ServiceConnectivityResources/res/values-hi/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-hi/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"सिस्टम कनेक्टिविटी के संसाधन"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"वाई-फ़ाई नेटवर्क में साइन इन करें"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"नेटवर्क में साइन इन करें"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> का इंटरनेट नहीं चल रहा है"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"विकल्पों के लिए टैप करें"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"मोबाइल नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"इस नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> की कनेक्टिविटी सीमित है"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"फिर भी कनेक्ट करने के लिए टैप करें"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> पर ले जाया गया"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> से <xliff:g id="NEW_NETWORK">%2$s</xliff:g> पर ले जाया गया"</string>
+ <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="5454013645032700715">"मोबाइल डेटा"</item>
- <item msgid="6341719431034774569">"वाई-फ़ाई"</item>
- <item msgid="5081440868800877512">"ब्लूटूथ"</item>
- <item msgid="1160736166977503463">"ईथरनेट"</item>
- <item msgid="7347618872551558605">"वीपीएन"</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="7826330274368951740">"अज्ञात नेटवर्क टाइप"</string>
+ <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 24bb22f..6a6de4c 100644
--- a/service/ServiceConnectivityResources/res/values-hr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-hr/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resursi za povezivost sustava"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prijava na Wi-Fi mrežu"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Prijava na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Dodirnite da biste se ipak povezali"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Prelazak na drugu mrežu: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobilni podaci"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"nepoznata vrsta mreže"</string>
+ <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 47a1142..1d39d30 100644
--- a/service/ServiceConnectivityResources/res/values-hu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-hu/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Rendszerkapcsolat erőforrásai"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Bejelentkezés Wi-Fi hálózatba"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Bejelentkezés a hálózatba"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"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="1229067002306296104">"Koppintson a beállítások megjelenítéséhez"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"A mobilhálózaton nincs internet-hozzáférés"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"A hálózaton nincs internet-hozzáférés"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"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="6975752539442533034">"Koppintson, ha mindenképpen csatlakozni szeretne"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Átváltva erre: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"Á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="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="5454013645032700715">"mobiladatok"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ismeretlen hálózati típus"</string>
+ <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 dd951e8..99386d4 100644
--- a/service/ServiceConnectivityResources/res/values-hy/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-hy/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Մուտք գործեք Wi-Fi ցանց"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Մուտք գործեք ցանց"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցը չունի մուտք ինտերնետին"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Հպեք՝ ընտրանքները տեսնելու համար"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Բջջային ցանցը չի ապահովում ինտերնետ կապ"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Ցանցը միացված չէ ինտերնետին"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Մասնավոր DNS սերվերն անհասանելի է"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցի կապը սահմանափակ է"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Հպեք՝ միանալու համար"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Անցել է <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ցանցի"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Երբ <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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ցանցից անցել է <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ցանցի"</string>
+ <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="5454013645032700715">"բջջային ինտերնետ"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ցանցի անհայտ տեսակ"</string>
+ <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 d559f6b..f47d257 100644
--- a/service/ServiceConnectivityResources/res/values-in/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-in/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resource Konektivitas Sistem"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Login ke jaringan Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Login ke jaringan"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tidak memiliki akses internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Ketuk untuk melihat opsi"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Jaringan seluler tidak memiliki akses internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Jaringan tidak memiliki akses internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Server DNS pribadi tidak dapat diakses"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> memiliki konektivitas terbatas"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ketuk untuk tetap menyambungkan"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Dialihkan ke <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Dialihkan dari <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ke <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"data seluler"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"jenis jaringan yang tidak dikenal"</string>
+ <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 877c85f..eeba231 100644
--- a/service/ServiceConnectivityResources/res/values-is/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-is/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Tengigögn kerfis"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Skrá inn á Wi-Fi net"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Skrá inn á net"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> er ekki með internetaðgang"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Ýttu til að sjá valkosti"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Farsímakerfið er ekki tengt við internetið"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Netkerfið er ekki tengt við internetið"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Ekki næst í DNS-einkaþjón"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Tengigeta <xliff:g id="NETWORK_SSID">%1$s</xliff:g> er takmörkuð"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ýttu til að tengjast samt"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Skipt yfir á <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Skipt úr <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> yfir í <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"farsímagögn"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"óþekkt tegund netkerfis"</string>
+ <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 bcac393..ec3ff8c 100644
--- a/service/ServiceConnectivityResources/res/values-it/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-it/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Risorse per connettività di sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Accedi a rete Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Accedi alla rete"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ha accesso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tocca per le opzioni"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"La rete mobile non ha accesso a Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"La rete non ha accesso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Non è possibile accedere al server DNS privato"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ha una connettività limitata"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tocca per connettere comunque"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Passato a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Passato da <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"dati mobili"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"tipo di rete sconosciuto"</string>
+ <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 d6684ce..d123ebb 100644
--- a/service/ServiceConnectivityResources/res/values-iw/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-iw/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"משאבי קישוריות מערכת"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"היכנס לרשת Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"היכנס לרשת"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"ל-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> אין גישה לאינטרנט"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"הקש לקבלת אפשרויות"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"לרשת הסלולרית אין גישה לאינטרנט"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"לרשת אין גישה לאינטרנט"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"לא ניתן לגשת לשרת DNS הפרטי"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"הקישוריות של <xliff:g id="NETWORK_SSID">%1$s</xliff:g> מוגבלת"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"כדי להתחבר למרות זאת יש להקיש"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"מעבר אל <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"המכשיר משתמש ברשת <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="8831325515040986641">"עבר מרשת <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> לרשת <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"חבילת גלישה"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"אתרנט"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"סוג רשת לא מזוהה"</string>
+ <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 fa4a30a..7bb6f85 100644
--- a/service/ServiceConnectivityResources/res/values-ja/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ja/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"システム接続リソース"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fiネットワークにログイン"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ネットワークにログインしてください"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> はインターネットにアクセスできません"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"タップしてその他のオプションを表示"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"モバイル ネットワークがインターネットに接続されていません"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"ネットワークがインターネットに接続されていません"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"プライベート DNS サーバーにアクセスできません"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> の接続が制限されています"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"接続するにはタップしてください"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"「<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>」に切り替えました"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"デバイスで「<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="8831325515040986641">"「<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>」から「<xliff:g id="NEW_NETWORK">%2$s</xliff:g>」に切り替えました"</string>
+ <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="5454013645032700715">"モバイルデータ"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"イーサネット"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"不明なネットワーク タイプ"</string>
+ <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 4183310..f42c567 100644
--- a/service/ServiceConnectivityResources/res/values-ka/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ka/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"სისტემის კავშირის რესურსები"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi ქსელთან დაკავშირება"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ქსელში შესვლა"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"შეეხეთ ვარიანტების სანახავად"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"მობილურ ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ის კავშირები შეზღუდულია"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"შეეხეთ, თუ მაინც გსურთ დაკავშირება"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"ახლა გამოიყენება <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"თუ <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="8831325515040986641">"ახლა გამოიყენება <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> (გამოიყენებოდა <xliff:g id="NEW_NETWORK">%2$s</xliff:g>)"</string>
+ <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="5454013645032700715">"მობილური ინტერნეტი"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"უცნობი ტიპის ქსელი"</string>
+ <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 54d5eb3..00c0f39 100644
--- a/service/ServiceConnectivityResources/res/values-kk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-kk/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Жүйе байланысы ресурстары"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi желісіне кіру"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Желіге кіру"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің интернетті пайдалану мүмкіндігі шектеулі."</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Опциялар үшін түртіңіз"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мобильдік желі интернетке қосылмаған."</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Желі интернетке қосылмаған."</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Жеке DNS серверіне кіру мүмкін емес."</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің қосылу мүмкіндігі шектеулі."</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Бәрібір жалғау үшін түртіңіз."</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> желісіне ауысты"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Құрылғы <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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
+ <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="5454013645032700715">"мобильдік деректер"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"желі түрі белгісіз"</string>
+ <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 bd778a1..fa06c5b 100644
--- a/service/ServiceConnectivityResources/res/values-km/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-km/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ធនធានតភ្ជាប់ប្រព័ន្ធ"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ចូលបណ្ដាញវ៉ាយហ្វាយ"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ចូលទៅបណ្តាញ"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ប៉ះសម្រាប់ជម្រើស"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"បណ្ដាញទូរសព្ទចល័តមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"បណ្ដាញមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មានការតភ្ជាប់មានកម្រិត"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"មិនអីទេ ចុចភ្ជាប់ចុះ"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"បានប្តូរទៅ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"ឧបករណ៍ប្រើ <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="8831325515040986641">"បានប្តូរពី <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ទៅ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"ទិន្នន័យទូរសព្ទចល័ត"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"ប៊្លូធូស"</item>
- <item msgid="1160736166977503463">"អ៊ីសឺរណិត"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ប្រភេទបណ្តាញដែលមិនស្គាល់"</string>
+ <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 7f3a420..cde8fac 100644
--- a/service/ServiceConnectivityResources/res/values-kn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-kn/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ಸಿಸ್ಟಂ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆ ಮಾಹಿತಿಯ ಮೂಲಗಳು"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"ಮೊಬೈಲ್ ನೆಟ್ವರ್ಕ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"ನೆಟ್ವರ್ಕ್ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಸೀಮಿತ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿದೆ"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ಹೇಗಾದರೂ ಸಂಪರ್ಕಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ರಿಂದ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
+ <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="5454013645032700715">"ಮೊಬೈಲ್ ಡೇಟಾ"</item>
- <item msgid="6341719431034774569">"ವೈ-ಫೈ"</item>
- <item msgid="5081440868800877512">"ಬ್ಲೂಟೂತ್"</item>
- <item msgid="1160736166977503463">"ಇಥರ್ನೆಟ್"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ಅಪರಿಚಿತ ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರ"</string>
+ <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 a763cc5..eef59a9 100644
--- a/service/ServiceConnectivityResources/res/values-ko/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ko/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"시스템 연결 리소스"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi 네트워크에 로그인"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"네트워크에 로그인"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>이(가) 인터넷에 액세스할 수 없습니다."</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"탭하여 옵션 보기"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"모바일 네트워크에 인터넷이 연결되어 있지 않습니다."</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"네트워크에 인터넷이 연결되어 있지 않습니다."</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>에서 연결을 제한했습니다."</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"계속 연결하려면 탭하세요."</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>(으)로 전환"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>에서 <xliff:g id="NEW_NETWORK">%2$s</xliff:g>(으)로 전환"</string>
+ <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="5454013645032700715">"모바일 데이터"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"블루투스"</item>
- <item msgid="1160736166977503463">"이더넷"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"알 수 없는 네트워크 유형"</string>
+ <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 3550af8..0027c8a 100644
--- a/service/ServiceConnectivityResources/res/values-ky/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ky/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Тутумдун байланыш булагы"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi түйүнүнө кирүү"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Тармакка кирүү"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мобилдик Интернет жок"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Тармактын Интернет жок"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Жеке DNS сервери жеткиликсиз"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> байланышы чектелген"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Баары бир туташуу үчүн таптаңыз"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> тармагына которуштурулду"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> дегенден <xliff:g id="NEW_NETWORK">%2$s</xliff:g> тармагына которуштурулду"</string>
+ <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="5454013645032700715">"мобилдик трафик"</item>
- <item msgid="6341719431034774569">"Wi‑Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"белгисиз тармак түрү"</string>
+ <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 4b3056f..64419f9 100644
--- a/service/ServiceConnectivityResources/res/values-lo/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-lo/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ແຫຼ່ງຂໍ້ມູນການເຊື່ອມຕໍ່ລະບົບ"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ເຂົ້າສູ່ລະບົບເຄືອຂ່າຍ Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"ເຄືອຂ່າຍມືຖືບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"ເຄືອຂ່າຍບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ມີການເຊື່ອມຕໍ່ທີ່ຈຳກັດ"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ແຕະເພື່ອຢືນຢັນການເຊື່ອມຕໍ່"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"ສະຫຼັບໄປໃຊ້ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ແລ້ວ"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"ອຸປະກອນຈະໃຊ້ <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="8831325515040986641">"ສະຫຼັບຈາກ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ໄປໃຊ້ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ແລ້ວ"</string>
+ <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="5454013645032700715">"ອິນເຕີເນັດມືຖື"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"ອີເທີເນັດ"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ບໍ່ຮູ້ຈັກປະເພດເຄືອຂ່າຍ"</string>
+ <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 8eb41f1..f73f142 100644
--- a/service/ServiceConnectivityResources/res/values-lt/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-lt/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prisijungti prie „Wi-Fi“ tinklo"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Prisijungti prie tinklo"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ negali pasiekti interneto"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Palieskite, kad būtų rodomos parinktys."</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiliojo ryšio tinkle nėra prieigos prie interneto"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Tinkle nėra prieigos prie interneto"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Privataus DNS serverio negalima pasiekti"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ ryšys apribotas"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Palieskite, jei vis tiek norite prisijungti"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Perjungta į tinklą <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Į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="8831325515040986641">"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="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="5454013645032700715">"mobiliojo ryšio duomenys"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Eternetas"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"nežinomas tinklo tipas"</string>
+ <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 0647a4f..9d26c40 100644
--- a/service/ServiceConnectivityResources/res/values-lv/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-lv/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sistēmas savienojamības resursi"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Pierakstieties Wi-Fi tīklā"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Pierakstīšanās tīklā"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nav piekļuves internetam"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Pieskarieties, lai skatītu iespējas."</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilajā tīklā nav piekļuves internetam."</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Tīklā nav piekļuves internetam."</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Nevar piekļūt privātam DNS serverim."</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ir ierobežota savienojamība"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Lai tik un tā izveidotu savienojumu, pieskarieties"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Pārslēdzās uz tīklu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobilie dati"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"nezināms tīkla veids"</string>
+ <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 b0024e2..fb105e0 100644
--- a/service/ServiceConnectivityResources/res/values-mk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-mk/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Најавете се на мрежа на Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Најавете се на мрежа"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема интернет-пристап"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Допрете за опции"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мобилната мрежа нема интернет-пристап"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Мрежата нема интернет-пристап"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Не може да се пристапи до приватниот DNS-сервер"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена поврзливост"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Допрете за да се поврзете и покрај тоа"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Префрлено на <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Уредот користи <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="8831325515040986641">"Префрлено од <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"мобилен интернет"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Етернет"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"непознат тип мрежа"</string>
+ <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 8ce7667..9a51238 100644
--- a/service/ServiceConnectivityResources/res/values-ml/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ml/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"സിസ്റ്റം കണക്റ്റിവിറ്റി ഉറവിടങ്ങൾ"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"വൈഫൈ നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"മൊബെെൽ നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് പരിമിതമായ കണക്റ്റിവിറ്റി ഉണ്ട്"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ഏതുവിധേനയും കണക്റ്റ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> എന്നതിലേക്ക് മാറി"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> നെറ്റ്വർക്കിൽ നിന്ന് <xliff:g id="NEW_NETWORK">%2$s</xliff:g> നെറ്റ്വർക്കിലേക്ക് മാറി"</string>
+ <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="5454013645032700715">"മൊബൈൽ ഡാറ്റ"</item>
- <item msgid="6341719431034774569">"വൈഫൈ"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"ഇതർനെറ്റ്"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"അജ്ഞാതമായ നെറ്റ്വർക്ക് തരം"</string>
+ <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 be8b592..8372533 100644
--- a/service/ServiceConnectivityResources/res/values-mn/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-mn/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Системийн холболтын нөөцүүд"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi сүлжээнд нэвтэрнэ үү"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Сүлжээнд нэвтэрнэ үү"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Сонголт хийхийн тулд товшино уу"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Сүлжээнд интернэт хандалт байхгүй байна"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Хувийн DNS серверт хандах боломжгүй байна"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ямар ч тохиолдолд холбогдохын тулд товших"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> руу шилжүүлсэн"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-с <xliff:g id="NEW_NETWORK">%2$s</xliff:g> руу шилжүүлсэн"</string>
+ <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="5454013645032700715">"мобайл дата"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Этернэт"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"үл мэдэгдэх сүлжээний төрөл"</string>
+ <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 fe7df84..658b19b 100644
--- a/service/ServiceConnectivityResources/res/values-mr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-mr/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"सिस्टम कनेक्टिव्हिटी चे स्रोत"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"वाय-फाय नेटवर्कमध्ये साइन इन करा"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"नेटवर्कवर साइन इन करा"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला इंटरनेट अॅक्सेस नाही"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"पर्यायांसाठी टॅप करा"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"मोबाइल नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला मर्यादित कनेक्टिव्हिटी आहे"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"तरीही कनेक्ट करण्यासाठी टॅप करा"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> वर स्विच केले"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> वरून <xliff:g id="NEW_NETWORK">%2$s</xliff:g> वर स्विच केले"</string>
+ <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="5454013645032700715">"मोबाइल डेटा"</item>
- <item msgid="6341719431034774569">"वाय-फाय"</item>
- <item msgid="5081440868800877512">"ब्लूटूथ"</item>
- <item msgid="1160736166977503463">"इथरनेट"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"अज्ञात नेटवर्क प्रकार"</string>
+ <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 54b49a2..84b242c 100644
--- a/service/ServiceConnectivityResources/res/values-ms/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ms/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sumber Kesambungan Sistem"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Log masuk ke rangkaian Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Log masuk ke rangkaian"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiada akses Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Ketik untuk mendapatkan pilihan"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Rangkaian mudah alih tiada akses Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Rangkaian tiada akses Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Pelayan DNS peribadi tidak boleh diakses"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> mempunyai kesambungan terhad"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ketik untuk menyambung juga"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Beralih kepada <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Beralih daripada <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kepada <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"data mudah alih"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"jenis rangkaian tidak diketahui"</string>
+ <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 15b75f0..6832263 100644
--- a/service/ServiceConnectivityResources/res/values-my/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-my/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"စနစ်ချိတ်ဆက်နိုင်မှု ရင်းမြစ်များ"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ဝိုင်ဖိုင်ကွန်ရက်သို့ လက်မှတ်ထိုးဝင်ပါ"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"မိုဘိုင်းကွန်ရက်တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"ကွန်ရက်တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် ချိတ်ဆက်မှုကို ကန့်သတ်ထားသည်"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"မည်သို့ပင်ဖြစ်စေ ချိတ်ဆက်ရန် တို့ပါ"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> မှ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
+ <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="5454013645032700715">"မိုဘိုင်းဒေတာ"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"ဘလူးတုသ်"</item>
- <item msgid="1160736166977503463">"အီသာနက်"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"အမည်မသိကွန်ရက်အမျိုးအစား"</string>
+ <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 a561def..00a0728 100644
--- a/service/ServiceConnectivityResources/res/values-nb/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-nb/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ressurser for systemtilkobling"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Logg på Wi-Fi-nettverket"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Logg på nettverk"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internettilkobling"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Trykk for å få alternativer"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilnettverket har ingen internettilgang"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Nettverket har ingen internettilgang"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Den private DNS-tjeneren kan ikke nås"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrenset tilkobling"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Trykk for å koble til likevel"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Byttet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Byttet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"mobildata"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"en ukjent nettverkstype"</string>
+ <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 f74542d..2eaf162 100644
--- a/service/ServiceConnectivityResources/res/values-ne/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ne/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"सिस्टम कनेक्टिभिटीका स्रोतहरू"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi नेटवर्कमा साइन इन गर्नुहोस्"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"सञ्जालमा साइन इन गर्नुहोस्"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"मोबाइल नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को जडान सीमित छ"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"जसरी भए पनि जडान गर्न ट्याप गर्नुहोस्"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> मा बदल्नुहोस्"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> बाट <xliff:g id="NEW_NETWORK">%2$s</xliff:g> मा परिवर्तन गरियो"</string>
+ <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="5454013645032700715">"मोबाइल डेटा"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"ब्लुटुथ"</item>
- <item msgid="1160736166977503463">"इथरनेट"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"नेटवर्कको कुनै अज्ञात प्रकार"</string>
+ <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 0f3203b..394c552 100644
--- a/service/ServiceConnectivityResources/res/values-nl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-nl/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resources voor systeemconnectiviteit"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Inloggen bij wifi-netwerk"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Inloggen bij netwerk"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft geen internettoegang"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tik voor opties"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiel netwerk heeft geen internettoegang"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Netwerk heeft geen internettoegang"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Geen toegang tot privé-DNS-server"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft beperkte connectiviteit"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tik om toch verbinding te maken"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Overgeschakeld naar <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Overgeschakeld van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> naar <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"mobiele data"</item>
- <item msgid="6341719431034774569">"Wifi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"een onbekend netwerktype"</string>
+ <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 ecf4d69..8b85884 100644
--- a/service/ServiceConnectivityResources/res/values-or/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-or/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ସିଷ୍ଟମର ସଂଯୋଗ ସମ୍ବନ୍ଧିତ ରିସୋର୍ସଗୁଡ଼ିକ"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"ମୋବାଇଲ୍ ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ସୀମିତ ସଂଯୋଗ ଅଛି"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ତଥାପି ଯୋଗାଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ରୁ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
+ <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="5454013645032700715">"ମୋବାଇଲ ଡାଟା"</item>
- <item msgid="6341719431034774569">"ୱାଇ-ଫାଇ"</item>
- <item msgid="5081440868800877512">"ବ୍ଲୁଟୁଥ୍"</item>
- <item msgid="1160736166977503463">"ଇଥରନେଟ୍"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ଏକ ଅଜଣା ନେଟୱାର୍କ ପ୍ରକାର"</string>
+ <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 4328054..9f71cac 100644
--- a/service/ServiceConnectivityResources/res/values-pa/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pa/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ਸਿਸਟਮ ਕਨੈਕਟੀਵਿਟੀ ਸਰੋਤ"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਸੀਮਤ ਕਨੈਕਟੀਵਿਟੀ ਹੈ"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ਫਿਰ ਵੀ ਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"ਬਦਲਕੇ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ਲਿਆਂਦਾ ਗਿਆ"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ਤੋਂ ਬਦਲਕੇ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> \'ਤੇ ਕੀਤਾ ਗਿਆ"</string>
+ <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="5454013645032700715">"ਮੋਬਾਈਲ ਡਾਟਾ"</item>
- <item msgid="6341719431034774569">"ਵਾਈ-ਫਾਈ"</item>
- <item msgid="5081440868800877512">"ਬਲੂਟੁੱਥ"</item>
- <item msgid="1160736166977503463">"ਈਥਰਨੈੱਟ"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ਕੋਈ ਅਗਿਆਤ ਨੈੱਟਵਰਕ ਦੀ ਕਿਸਮ"</string>
+ <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 e6b3a0c..cc84e29 100644
--- a/service/ServiceConnectivityResources/res/values-pl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pl/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Zasoby systemowe dotyczące łączności"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Zaloguj się w sieci Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Zaloguj się do sieci"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nie ma dostępu do internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Kliknij, by wyświetlić opcje"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Sieć komórkowa nie ma dostępu do internetu"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Sieć nie ma dostępu do internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Brak dostępu do prywatnego serwera DNS"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ma ograniczoną łączność"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Kliknij, by mimo to nawiązać połączenie"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Zmieniono na połączenie typu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobilna transmisja danych"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"nieznany typ sieci"</string>
+ <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 f1d0bc0..3c15a76 100644
--- a/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividade do sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Fazer login na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Fazer login na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toque para ver opções"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Não é possível acessar o servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toque para conectar mesmo assim"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"dados móveis"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"um tipo de rede desconhecido"</string>
+ <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 163d70b..48dde75 100644
--- a/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conetividade do sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Iniciar sessão na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Início de sessão na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toque para obter mais opções"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Não é possível aceder ao servidor DNS."</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conetividade limitada."</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toque para ligar mesmo assim."</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Mudou para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Mudou de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"dados móveis"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"um tipo de rede desconhecido"</string>
+ <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 f1d0bc0..3c15a76 100644
--- a/service/ServiceConnectivityResources/res/values-pt/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-pt/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividade do sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Fazer login na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Fazer login na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toque para ver opções"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Não é possível acessar o servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toque para conectar mesmo assim"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"dados móveis"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"um tipo de rede desconhecido"</string>
+ <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 221261c..fa5848f 100644
--- a/service/ServiceConnectivityResources/res/values-ro/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ro/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resurse pentru conectivitatea sistemului"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Conectați-vă la rețeaua Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Conectați-vă la rețea"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Atingeți pentru opțiuni"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Rețeaua mobilă nu are acces la internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Rețeaua nu are acces la internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Serverul DNS privat nu poate fi accesat"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Atingeți pentru a vă conecta oricum"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"date mobile"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"un tip de rețea necunoscut"</string>
+ <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 ba179b7..2e074ed 100644
--- a/service/ServiceConnectivityResources/res/values-ru/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ru/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Подключение к Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Регистрация в сети"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Сеть \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" не подключена к Интернету"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Нажмите, чтобы показать варианты."</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мобильная сеть не подключена к Интернету"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Сеть не подключена к Интернету"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Доступа к частному DNS-серверу нет."</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Подключение к сети \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" ограничено"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Нажмите, чтобы подключиться"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Новое подключение: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Устройство использует <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="8831325515040986641">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
+ <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="5454013645032700715">"мобильный интернет"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"неизвестный тип сети"</string>
+ <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 1c493a7..a4f720a 100644
--- a/service/ServiceConnectivityResources/res/values-si/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-si/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"පද්ධති සබැඳුම් හැකියා සම්පත්"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi ජාලයට පුරනය වන්න"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ජාලයට පුරනය වන්න"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"විකල්ප සඳහා තට්ටු කරන්න"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"ජංගම ජාලවලට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"ජාලයට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට සීමිත සබැඳුම් හැකියාවක් ඇත"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"කෙසේ වෙතත් ඉදිරියට යාමට තට්ටු කරන්න"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> වෙත මාරු විය"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"උපාංගය <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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> සිට <xliff:g id="NEW_NETWORK">%2$s</xliff:g> වෙත මාරු විය"</string>
+ <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="5454013645032700715">"ජංගම දත්ත"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"බ්ලූටූත්"</item>
- <item msgid="1160736166977503463">"ඊතර්නෙට්"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"නොදන්නා ජාල වර්ගයකි"</string>
+ <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 1b9313a..432b670 100644
--- a/service/ServiceConnectivityResources/res/values-sk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sk/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Zdroje možností pripojenia systému"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prihlásiť sa do siete Wi‑Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Prihlásenie do siete"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá prístup k internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Klepnutím získate možnosti"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilná sieť nemá prístup k internetu"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Sieť nemá prístup k internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> má obmedzené pripojenie"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ak sa chcete aj napriek tomu pripojiť, klepnite"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Prepnuté na sieť: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobilné dáta"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"neznámy typ siete"</string>
+ <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 739fb8e..b727614 100644
--- a/service/ServiceConnectivityResources/res/values-sl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sl/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Viri povezljivosti sistema"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prijavite se v omrežje Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Prijava v omrežje"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Omrežje <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nima dostopa do interneta"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Dotaknite se za možnosti"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilno omrežje nima dostopa do interneta"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Omrežje nima dostopa do interneta"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Povezljivost omrežja <xliff:g id="NETWORK_SSID">%1$s</xliff:g> je omejena"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Dotaknite se, da kljub temu vzpostavite povezavo"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Preklopljeno na omrežje vrste <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"prenos podatkov v mobilnem omrežju"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"neznana vrsta omrežja"</string>
+ <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 cf8cf3b..385c75c 100644
--- a/service/ServiceConnectivityResources/res/values-sq/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sq/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Burimet e lidhshmërisë së sistemit"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Identifikohu në rrjetin Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Identifikohu në rrjet"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nuk ka qasje në internet"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Trokit për opsionet"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Rrjeti celular nuk ka qasje në internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Rrjeti nuk ka qasje në internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Serveri privat DNS nuk mund të qaset"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ka lidhshmëri të kufizuar"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Trokit për t\'u lidhur gjithsesi"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Kaloi te <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Kaloi nga <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> te <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"të dhënat celulare"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Eternet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"një lloj rrjeti i panjohur"</string>
+ <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 1f7c95c..928dc79 100644
--- a/service/ServiceConnectivityResources/res/values-sr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sr/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ресурси за повезивање са системом"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Пријављивање на WiFi мрежу"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Пријавите се на мрежу"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема приступ интернету"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Додирните за опције"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мобилна мрежа нема приступ интернету"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Мрежа нема приступ интернету"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Приступ приватном DNS серверу није успео"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничену везу"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Додирните да бисте се ипак повезали"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Прешли сте на тип мреже <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Уређај користи тип мреже <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="8831325515040986641">"Прешли сте са типа мреже <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на тип мреже <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"мобилни подаци"</item>
- <item msgid="6341719431034774569">"WiFi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Етернет"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"непознат тип мреже"</string>
+ <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 7314005..d714124 100644
--- a/service/ServiceConnectivityResources/res/values-sv/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sv/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resurser för systemanslutning"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Logga in på ett Wi-Fi-nätverk"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Logga in på nätverket"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetanslutning"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tryck för alternativ"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilnätverket har ingen internetanslutning"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Nätverket har ingen internetanslutning"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Det går inte att komma åt den privata DNS-servern."</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begränsad anslutning"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tryck för att ansluta ändå"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Byte av nätverk till <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"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="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="5454013645032700715">"mobildata"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"en okänd nätverkstyp"</string>
+ <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 5c4d594..15d6cab 100644
--- a/service/ServiceConnectivityResources/res/values-sw/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-sw/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Nyenzo za Muunganisho wa Mfumo"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Ingia kwa mtandao wa Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Ingia katika mtandao"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> haina uwezo wa kufikia intaneti"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Gusa ili upate chaguo"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mtandao wa simu hauna uwezo wa kufikia intaneti"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Mtandao hauna uwezo wa kufikia intaneti"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ina muunganisho unaofikia huduma chache."</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Gusa ili uunganishe tu"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"data ya mtandao wa simu"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethaneti"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"aina ya mtandao isiyojulikana"</string>
+ <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 90f89c9..9850a35 100644
--- a/service/ServiceConnectivityResources/res/values-ta/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ta/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"சிஸ்டம் இணைப்பு மூலங்கள்"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"வைஃபை நெட்வொர்க்கில் உள்நுழையவும்"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"நெட்வொர்க்கில் உள்நுழையவும்"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"விருப்பங்களுக்கு, தட்டவும்"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"மொபைல் நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> வரம்பிற்கு உட்பட்ட இணைப்புநிலையைக் கொண்டுள்ளது"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"எப்படியேனும் இணைப்பதற்குத் தட்டவும்"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> இலிருந்து <xliff:g id="NEW_NETWORK">%2$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
+ <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="5454013645032700715">"மொபைல் டேட்டா"</item>
- <item msgid="6341719431034774569">"வைஃபை"</item>
- <item msgid="5081440868800877512">"புளூடூத்"</item>
- <item msgid="1160736166977503463">"ஈதர்நெட்"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"தெரியாத நெட்வொர்க் வகை"</string>
+ <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 c69b599..f7182a8 100644
--- a/service/ServiceConnectivityResources/res/values-te/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-te/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"సిస్టమ్ కనెక్టివిటీ రిసోర్స్లు"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ఎంపికల కోసం నొక్కండి"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"మొబైల్ నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> పరిమిత కనెక్టివిటీని కలిగి ఉంది"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ఏదేమైనా కనెక్ట్ చేయడానికి నొక్కండి"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>కి మార్చబడింది"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"పరికరం <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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> నుండి <xliff:g id="NEW_NETWORK">%2$s</xliff:g>కి మార్చబడింది"</string>
+ <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="5454013645032700715">"మొబైల్ డేటా"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"బ్లూటూత్"</item>
- <item msgid="1160736166977503463">"ఈథర్నెట్"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"తెలియని నెట్వర్క్ రకం"</string>
+ <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 eee5a35..7049309 100644
--- a/service/ServiceConnectivityResources/res/values-th/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-th/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ทรัพยากรการเชื่อมต่อของระบบ"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"ลงชื่อเข้าใช้เครือข่าย WiFi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"ลงชื่อเข้าใช้เครือข่าย"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"แตะเพื่อดูตัวเลือก"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"เครือข่ายมือถือไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"เครือข่ายไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> มีการเชื่อมต่อจำกัด"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"แตะเพื่อเชื่อมต่อ"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"เปลี่ยนเป็น <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"อุปกรณ์จะใช้ <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="8831325515040986641">"เปลี่ยนจาก <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> เป็น <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"อินเทอร์เน็ตมือถือ"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"บลูทูธ"</item>
- <item msgid="1160736166977503463">"อีเทอร์เน็ต"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"ประเภทเครือข่ายที่ไม่รู้จัก"</string>
+ <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 8d665fe..c866fd4 100644
--- a/service/ServiceConnectivityResources/res/values-tl/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-tl/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Mga Resource ng Pagkakonekta ng System"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Mag-sign in sa Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Mag-sign in sa network"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Walang access sa internet ang <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"I-tap para sa mga opsyon"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Walang access sa internet ang mobile network"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Walang access sa internet ang network"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Hindi ma-access ang pribadong DNS server"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Limitado ang koneksyon ng <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"I-tap para kumonekta pa rin"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Lumipat sa <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"mobile data"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"isang hindi kilalang uri ng network"</string>
+ <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 cfb7632..c4930a8 100644
--- a/service/ServiceConnectivityResources/res/values-tr/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-tr/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sistem Bağlantı Kaynakları"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Kablosuz ağda oturum açın"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Ağda oturum açın"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<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="1229067002306296104">"Seçenekler için dokunun"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobil ağın internet bağlantısı yok"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Ağın internet bağlantısı yok"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Gizli DNS sunucusuna erişilemiyor"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sınırlı bağlantıya sahip"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Yine de bağlanmak için dokunun"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ağına geçildi"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"<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="8831325515040986641">"<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="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="5454013645032700715">"mobil veri"</item>
- <item msgid="6341719431034774569">"Kablosuz"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"bilinmeyen ağ türü"</string>
+ <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 c5da746..8811263 100644
--- a/service/ServiceConnectivityResources/res/values-uk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-uk/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ресурси для підключення системи"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Вхід у мережу Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Вхід у мережу"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"Мережа <xliff:g id="NETWORK_SSID">%1$s</xliff:g> не має доступу до Інтернету"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Торкніться, щоб відкрити опції"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Мобільна мережа не має доступу до Інтернету"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Мережа не має доступу до Інтернету"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Немає доступу до приватного DNS-сервера"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"Підключення до мережі <xliff:g id="NETWORK_SSID">%1$s</xliff:g> обмежено"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Натисніть, щоб усе одно підключитися"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Пристрій перейшов на мережу <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"Коли мережа <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="8831325515040986641">"Пристрій перейшов з мережі <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на мережу <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"мобільний Інтернет"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"невідомий тип мережі"</string>
+ <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 bd2a228..8f9656c 100644
--- a/service/ServiceConnectivityResources/res/values-ur/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ur/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"سسٹم کنیکٹوٹی کے وسائل"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi نیٹ ورک میں سائن ان کریں"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"نیٹ ورک میں سائن ان کریں"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"اختیارات کیلئے تھپتھپائیں"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"موبائل نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کی کنیکٹوٹی محدود ہے"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"بہر حال منسلک کرنے کے لیے تھپتھپائیں"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> پر سوئچ ہو گیا"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"جب <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="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> سے <xliff:g id="NEW_NETWORK">%2$s</xliff:g> پر سوئچ ہو گیا"</string>
+ <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="5454013645032700715">"موبائل ڈیٹا"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"بلوٹوتھ"</item>
- <item msgid="1160736166977503463">"ایتھرنیٹ"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"نامعلوم نیٹ ورک کی قسم"</string>
+ <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 567aa88..d7285ad 100644
--- a/service/ServiceConnectivityResources/res/values-uz/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-uz/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Tizim aloqa resurslari"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi tarmoqqa kirish"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Tarmoqqa kirish"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda internetga ruxsati yoʻq"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Variantlarni ko‘rsatish uchun bosing"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mobil tarmoq internetga ulanmagan"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Tarmoq internetga ulanmagan"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Xususiy DNS server ishlamayapti"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda aloqa cheklangan"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Baribir ulash uchun bosing"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Yangi ulanish: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"<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="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="5454013645032700715">"mobil internet"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"nomaʼlum tarmoq turi"</string>
+ <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 590b388..239fb81 100644
--- a/service/ServiceConnectivityResources/res/values-vi/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-vi/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Tài nguyên kết nối hệ thống"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Đăng nhập vào mạng Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Đăng nhập vào mạng"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<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="1229067002306296104">"Nhấn để biết tùy chọn"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Mạng di động không có quyền truy cập Internet"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Mạng không có quyền truy cập Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Không thể truy cập máy chủ DNS riêng tư"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<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="6975752539442533034">"Nhấn để tiếp tục kết nối"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Đã chuyển sang <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"Đã 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="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="5454013645032700715">"dữ liệu di động"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"Bluetooth"</item>
- <item msgid="1160736166977503463">"Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"loại mạng không xác định"</string>
+ <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 9d6cff9..e318c0b 100644
--- a/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"系统网络连接资源"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"登录到WLAN网络"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"登录到网络"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 无法访问互联网"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"点按即可查看相关选项"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"此移动网络无法访问互联网"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"此网络无法访问互联网"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"无法访问私人 DNS 服务器"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的连接受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"点按即可继续连接"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"已切换至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"设备会在<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="8831325515040986641">"已从<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切换至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"移动数据"</item>
- <item msgid="6341719431034774569">"WLAN"</item>
- <item msgid="5081440868800877512">"蓝牙"</item>
- <item msgid="1160736166977503463">"以太网"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"未知网络类型"</string>
+ <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 c84241c..af3dccd 100644
--- a/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"系統連線資源"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"登入 Wi-Fi 網絡"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"登入網絡"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>未有連接至互聯網"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"輕按即可查看選項"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"流動網絡並未連接互聯網"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"網絡並未連接互聯網"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"無法存取私人 DNS 伺服器"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>連線受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"仍要輕按以連結至此網絡"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"裝置會在 <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="8831325515040986641">"已從<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"流動數據"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"藍牙"</item>
- <item msgid="1160736166977503463">"以太網絡"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"不明網絡類型"</string>
+ <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 07540d1..6441707 100644
--- a/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"系統連線資源"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"登入 Wi-Fi 網路"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"登入網路"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 沒有網際網路連線"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"輕觸即可查看選項"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"這個行動網路沒有網際網路連線"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"這個網路沒有網際網路連線"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"無法存取私人 DNS 伺服器"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的連線能力受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"輕觸即可繼續連線"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"裝置會在無法連上「<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="8831325515040986641">"已從 <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> 切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
+ <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="5454013645032700715">"行動數據"</item>
- <item msgid="6341719431034774569">"Wi-Fi"</item>
- <item msgid="5081440868800877512">"藍牙"</item>
- <item msgid="1160736166977503463">"乙太網路"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"不明的網路類型"</string>
+ <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 19f390b..b59f0d1 100644
--- a/service/ServiceConnectivityResources/res/values-zu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-zu/strings.xml
@@ -17,27 +17,27 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Izinsiza Zokuxhumeka Zesistimu"</string>
- <string name="wifi_available_sign_in" msgid="5254156478006453593">"Ngena ngemvume kunethiwekhi ye-Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="7794369329839408792">"Ngena ngemvume kunethiwekhi"</string>
- <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <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="3961697321010262514">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ayinakho ukufinyelela kwe-inthanethi"</string>
- <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Thepha ukuze uthole izinketho"</string>
- <string name="mobile_no_internet" msgid="2262524005014119639">"Inethiwekhi yeselula ayinakho ukufinyelela kwe-inthanethi"</string>
- <string name="other_networks_no_internet" msgid="8226004998719563755">"Inethiwekhi ayinakho ukufinyelela kwenethiwekhi"</string>
- <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
- <string name="network_partial_connectivity" msgid="5957065286265771273">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> inokuxhumeka okukhawulelwe"</string>
- <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Thepha ukuze uxhume noma kunjalo"</string>
- <string name="network_switch_metered" msgid="2814798852883117872">"Kushintshelwe ku-<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="605546931076348229">"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="8831325515040986641">"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="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="5454013645032700715">"idatha yeselula"</item>
- <item msgid="6341719431034774569">"I-Wi-Fi"</item>
- <item msgid="5081440868800877512">"I-Bluetooth"</item>
- <item msgid="1160736166977503463">"I-Ethernet"</item>
- <item msgid="7347618872551558605">"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="7826330274368951740">"uhlobo olungaziwa lwenethiwekhi"</string>
+ <string name="network_switch_type_name_unknown" msgid="5116448402191972082">"uhlobo olungaziwa lwenethiwekhi"</string>
</resources>
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index e5d1a88..cc0cbcc 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -90,5 +90,9 @@
# From services-connectivity-shared-srcs
rule android.net.util.NetworkConstants* com.android.connectivity.@0
+# From modules-utils-statemachine
+rule com.android.internal.util.IState* com.android.connectivity.@0
+rule com.android.internal.util.State* com.android.connectivity.@0
+
# Remaining are connectivity sources in com.android.server and com.android.server.connectivity:
# TODO: move to a subpackage of com.android.connectivity (such as com.android.connectivity.server)
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..9603139
--- /dev/null
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -0,0 +1,361 @@
+/*
+ * 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 <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <net/if.h>
+#include <string>
+
+#include <netjniutils/netjniutils.h>
+
+#include "libclat/bpfhelper.h"
+#include "libclat/clatutils.h"
+#include "nativehelper/scoped_utf_chars.h"
+
+// Sync from system/netd/include/netid_client.h
+#define MARK_UNSET 0u
+
+#define DEVICEPREFIX "v4-"
+
+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;
+ }
+}
+
+int initTracker(const std::string& iface, const std::string& pfx96, const std::string& v4,
+ const std::string& v6, net::clat::ClatdTracker* output) {
+ strlcpy(output->iface, iface.c_str(), sizeof(output->iface));
+ output->ifIndex = if_nametoindex(iface.c_str());
+ if (output->ifIndex == 0) {
+ ALOGE("interface %s not found", output->iface);
+ return -1;
+ }
+
+ unsigned len = snprintf(output->v4iface, sizeof(output->v4iface),
+ "%s%s", DEVICEPREFIX, iface.c_str());
+ if (len >= sizeof(output->v4iface)) {
+ ALOGE("interface name too long '%s'", output->v4iface);
+ return -1;
+ }
+
+ output->v4ifIndex = if_nametoindex(output->v4iface);
+ if (output->v4ifIndex == 0) {
+ ALOGE("v4-interface %s not found", output->v4iface);
+ return -1;
+ }
+
+ if (!inet_pton(AF_INET6, pfx96.c_str(), &output->pfx96)) {
+ ALOGE("invalid IPv6 address specified for plat prefix: %s", pfx96.c_str());
+ return -1;
+ }
+
+ if (!inet_pton(AF_INET, v4.c_str(), &output->v4)) {
+ ALOGE("Invalid IPv4 address %s", v4.c_str());
+ return -1;
+ }
+
+ if (!inet_pton(AF_INET6, v6.c_str(), &output->v6)) {
+ ALOGE("Invalid source address %s", v6.c_str());
+ return -1;
+ }
+
+ return 0;
+}
+
+// TODO: fork clatd and rename to .._startClatd.
+static jint com_android_server_connectivity_ClatCoordinator_maybeStartBpf(
+ JNIEnv* env, jobject clazz, jobject tunJavaFd, jobject readSockJavaFd,
+ jobject writeSockJavaFd, jstring iface, jstring pfx96, jstring v4, jstring v6) {
+ ScopedUtfChars ifaceStr(env, iface);
+ ScopedUtfChars pfx96Str(env, pfx96);
+ ScopedUtfChars v4Str(env, v4);
+ ScopedUtfChars v6Str(env, v6);
+
+ // Start BPF if any
+ if (!net::clat::initMaps()) {
+ net::clat::ClatdTracker tracker = {};
+ if (!initTracker(ifaceStr.c_str(), pfx96Str.c_str(), v4Str.c_str(), v6Str.c_str(),
+ &tracker)) {
+ net::clat::maybeStartBpf(tracker);
+ }
+ }
+
+ return 0; // TODO: return forked clatd pid.
+}
+
+// TODO: stop clatd and rename to .._stopClatd.
+static void com_android_server_connectivity_ClatCoordinator_maybeStopBpf(JNIEnv* env, jobject clazz,
+ jstring iface, jstring pfx96,
+ jstring v4, jstring v6,
+ jint pid /* unused */) {
+ ScopedUtfChars ifaceStr(env, iface);
+ ScopedUtfChars pfx96Str(env, pfx96);
+ ScopedUtfChars v4Str(env, v4);
+ ScopedUtfChars v6Str(env, v6);
+
+ if (!net::clat::initMaps()) {
+ net::clat::ClatdTracker tracker = {};
+ if (!initTracker(ifaceStr.c_str(), pfx96Str.c_str(), v4Str.c_str(), v6Str.c_str(),
+ &tracker)) {
+ net::clat::maybeStopBpf(tracker);
+ }
+ }
+}
+
+/*
+ * 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},
+ {"native_maybeStartBpf",
+ "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/lang/"
+ "String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)com_android_server_connectivity_ClatCoordinator_maybeStartBpf},
+ {"native_maybeStopBpf",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V",
+ (void*)com_android_server_connectivity_ClatCoordinator_maybeStopBpf},
+};
+
+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/native/Android.bp b/service/native/Android.bp
new file mode 100644
index 0000000..5816318
--- /dev/null
+++ b/service/native/Android.bp
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+cc_library {
+ name: "libtraffic_controller",
+ defaults: ["netd_defaults"],
+ srcs: [
+ "TrafficController.cpp",
+ ],
+ header_libs: [
+ "bpf_connectivity_headers",
+ "bpf_headers",
+ "bpf_syscall_wrappers",
+ ],
+ static_libs: [
+ "libnetdutils",
+ // TrafficController would use the constants of INetd so that add
+ // netd_aidl_interface-lateststable-ndk.
+ "netd_aidl_interface-lateststable-ndk",
+ ],
+ shared_libs: [
+ // TODO: Find a good way to remove libbase.
+ "libbase",
+ "libcutils",
+ "libutils",
+ "liblog",
+ ],
+ export_include_dirs: ["include"],
+ sanitize: {
+ cfi: true,
+ },
+ apex_available: [
+ "com.android.tethering",
+ ],
+ min_sdk_version: "30",
+}
+
+cc_library_shared {
+ name: "libtraffic_controller_jni",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+ srcs: [
+ "jni/*.cpp",
+ ],
+ header_libs: [
+ "bpf_connectivity_headers",
+ ],
+ static_libs: [
+ "libnetdutils",
+ "libtraffic_controller",
+ "netd_aidl_interface-lateststable-ndk",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libutils",
+ "libnativehelper",
+ ],
+ apex_available: [
+ "com.android.tethering",
+ ],
+ min_sdk_version: "30",
+}
+
+cc_test {
+ name: "traffic_controller_unit_test",
+ test_suites: ["general-tests"],
+ require_root: true,
+ local_include_dirs: ["include"],
+ header_libs: [
+ "bpf_connectivity_headers",
+ ],
+ srcs: [
+ "TrafficControllerTest.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "liblog",
+ "libnetdutils",
+ "libtraffic_controller",
+ "libutils",
+ "netd_aidl_interface-lateststable-ndk",
+ ],
+}
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
new file mode 100644
index 0000000..cac545d
--- /dev/null
+++ b/service/native/TrafficController.cpp
@@ -0,0 +1,903 @@
+/*
+ * 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 "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 <map>
+#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 <private/android_filesystem_config.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() {
+ 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.
+ // TODO: Clean-up addInterface() after interface monitoring is in
+ // NetworkStatsService.
+ 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 uint32_t uid,
+ UidOwnerMatchType matchType, IptOp op) {
+ std::lock_guard guard(mMutex);
+ 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..f401636
--- /dev/null
+++ b/service/native/TrafficControllerTest.cpp
@@ -0,0 +1,750 @@
+/*
+ * 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.
+ *
+ * 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 <binder/Status.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 android::netdutils::Status;
+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);
+ }
+
+ Status updateUidOwnerMaps(const std::vector<uint32_t>& appUids,
+ UidOwnerMatchType matchType, TrafficController::IptOp op) {
+ Status ret(0);
+ for (auto uid : appUids) {
+ ret = mTc.updateUidOwnerMap(uid, matchType, op);
+ if(!isOk(ret)) break;
+ }
+ return ret;
+ }
+
+};
+
+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(updateUidOwnerMaps(appUids, PENALTY_BOX_MATCH,
+ TrafficController::IptOpInsert)));
+ expectUidOwnerMapValues(appUids, PENALTY_BOX_MATCH, 0);
+ ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, PENALTY_BOX_MATCH,
+ TrafficController::IptOpDelete)));
+ expectMapEmpty(mFakeUidOwnerMap);
+}
+
+TEST_F(TrafficControllerTest, TestAllowlistUidMatch) {
+ std::vector<uint32_t> appUids = {1000, 1001, 10012};
+ ASSERT_TRUE(isOk(updateUidOwnerMaps(appUids, HAPPY_BOX_MATCH, TrafficController::IptOpInsert)));
+ expectUidOwnerMapValues(appUids, HAPPY_BOX_MATCH, 0);
+ ASSERT_TRUE(isOk(updateUidOwnerMaps(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(updateUidOwnerMaps(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(updateUidOwnerMaps(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(updateUidOwnerMaps(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(updateUidOwnerMaps(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(updateUidOwnerMaps(appUids, HAPPY_BOX_MATCH,
+ TrafficController::IptOpDelete)));
+ expectMapEmpty(mFakeUidOwnerMap);
+
+ // Add denylist rules for appUids.
+ ASSERT_TRUE(isOk(updateUidOwnerMaps(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(updateUidOwnerMaps(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(updateUidOwnerMaps({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(updateUidOwnerMaps({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/Common.h b/service/native/include/Common.h
new file mode 100644
index 0000000..7c0b797
--- /dev/null
+++ b/service/native/include/Common.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
+// TODO: deduplicate with the constants in NetdConstants.h.
+#include <aidl/android/net/INetd.h>
+
+using aidl::android::net::INetd;
+
+enum FirewallRule { ALLOW = INetd::FIREWALL_RULE_ALLOW, DENY = INetd::FIREWALL_RULE_DENY };
+
+// ALLOWLIST means the firewall denies all by default, uids must be explicitly ALLOWed
+// DENYLIST means the firewall allows all by default, uids must be explicitly DENYed
+
+enum FirewallType { ALLOWLIST = INetd::FIREWALL_ALLOWLIST, DENYLIST = INetd::FIREWALL_DENYLIST };
+
+enum ChildChain {
+ NONE = INetd::FIREWALL_CHAIN_NONE,
+ DOZABLE = INetd::FIREWALL_CHAIN_DOZABLE,
+ STANDBY = INetd::FIREWALL_CHAIN_STANDBY,
+ POWERSAVE = INetd::FIREWALL_CHAIN_POWERSAVE,
+ RESTRICTED = INetd::FIREWALL_CHAIN_RESTRICTED,
+ INVALID_CHAIN
+};
diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h
new file mode 100644
index 0000000..c050871
--- /dev/null
+++ b/service/native/include/TrafficController.h
@@ -0,0 +1,203 @@
+/*
+ * 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 <set>
+#include <Common.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 {
+
+using netdutils::StatusOr;
+
+class TrafficController {
+ public:
+ /*
+ * Initialize the whole controller
+ */
+ netdutils::Status start();
+
+ 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 uint32_t uid,
+ 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);
+
+ 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
diff --git a/service/native/jni/com_android_server_BpfNetMaps.cpp b/service/native/jni/com_android_server_BpfNetMaps.cpp
new file mode 100644
index 0000000..7ab4d46
--- /dev/null
+++ b/service/native/jni/com_android_server_BpfNetMaps.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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 "TrafficControllerJni"
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <net/if.h>
+#include <vector>
+
+#include "TrafficController.h"
+#include "android-base/logging.h"
+#include "bpf_shared.h"
+#include "utils/Log.h"
+
+using android::net::TrafficController;
+using android::netdutils::Status;
+
+using UidOwnerMatchType::PENALTY_BOX_MATCH;
+using UidOwnerMatchType::HAPPY_BOX_MATCH;
+
+static android::net::TrafficController mTc;
+
+namespace android {
+
+static void native_init(JNIEnv* env, jobject clazz) {
+ Status status = mTc.start();
+ if (!isOk(status)) {
+ ALOGE("%s failed", __func__);
+ }
+}
+
+static jint native_addNaughtyApp(JNIEnv* env, jobject clazz, jint uid) {
+ const uint32_t appUids = static_cast<uint32_t>(abs(uid));
+ Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH,
+ TrafficController::IptOp::IptOpInsert);
+ if (!isOk(status)) {
+ ALOGE("%s failed, errer code = %d", __func__, status.code());
+ }
+ return (jint)status.code();
+}
+
+static jint native_removeNaughtyApp(JNIEnv* env, jobject clazz, jint uid) {
+ const uint32_t appUids = static_cast<uint32_t>(abs(uid));
+ Status status = mTc.updateUidOwnerMap(appUids, PENALTY_BOX_MATCH,
+ TrafficController::IptOp::IptOpDelete);
+ if (!isOk(status)) {
+ ALOGE("%s failed, errer code = %d", __func__, status.code());
+ }
+ return (jint)status.code();
+}
+
+static jint native_addNiceApp(JNIEnv* env, jobject clazz, jint uid) {
+ const uint32_t appUids = static_cast<uint32_t>(abs(uid));
+ Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH,
+ TrafficController::IptOp::IptOpInsert);
+ if (!isOk(status)) {
+ ALOGE("%s failed, errer code = %d", __func__, status.code());
+ }
+ return (jint)status.code();
+}
+
+static jint native_removeNiceApp(JNIEnv* env, jobject clazz, jint uid) {
+ const uint32_t appUids = static_cast<uint32_t>(abs(uid));
+ Status status = mTc.updateUidOwnerMap(appUids, HAPPY_BOX_MATCH,
+ TrafficController::IptOp::IptOpDelete);
+ if (!isOk(status)) {
+ ALOGD("%s failed, errer code = %d", __func__, status.code());
+ }
+ return (jint)status.code();
+}
+
+static jint native_setChildChain(JNIEnv* env, jobject clazz, jint childChain, jboolean enable) {
+ auto chain = static_cast<ChildChain>(childChain);
+ int res = mTc.toggleUidOwnerMap(chain, enable);
+ if (res) {
+ ALOGE("%s failed, error code = %d", __func__, res);
+ }
+ return (jint)res;
+}
+
+static jint native_replaceUidChain(JNIEnv* env, jobject clazz, jstring name, jboolean isAllowlist,
+ jintArray jUids) {
+ const ScopedUtfChars chainNameUtf8(env, name);
+ if (chainNameUtf8.c_str() == nullptr) {
+ return -EINVAL;
+ }
+ const std::string chainName(chainNameUtf8.c_str());
+
+ ScopedIntArrayRO uids(env, jUids);
+ if (uids.get() == nullptr) {
+ return -EINVAL;
+ }
+
+ size_t size = uids.size();
+ std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
+ int res = mTc.replaceUidOwnerMap(chainName, isAllowlist, data);
+ if (res) {
+ ALOGE("%s failed, error code = %d", __func__, res);
+ }
+ return (jint)res;
+}
+
+static FirewallType 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;
+ }
+}
+
+static jint native_setUidRule(JNIEnv* env, jobject clazz, jint childChain, jint uid,
+ jint firewallRule) {
+ auto chain = static_cast<ChildChain>(childChain);
+ auto rule = static_cast<FirewallRule>(firewallRule);
+ FirewallType fType = getFirewallType(chain);
+
+ int res = mTc.changeUidOwnerRule(chain, uid, rule, fType);
+ if (res) {
+ ALOGE("%s failed, error code = %d", __func__, res);
+ }
+ return (jint)res;
+}
+
+static jint native_addUidInterfaceRules(JNIEnv* env, jobject clazz, jstring ifName,
+ jintArray jUids) {
+ const ScopedUtfChars ifNameUtf8(env, ifName);
+ if (ifNameUtf8.c_str() == nullptr) {
+ return -EINVAL;
+ }
+ const std::string interfaceName(ifNameUtf8.c_str());
+ const int ifIndex = if_nametoindex(interfaceName.c_str());
+
+ ScopedIntArrayRO uids(env, jUids);
+ if (uids.get() == nullptr) {
+ return -EINVAL;
+ }
+
+ size_t size = uids.size();
+ std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
+ Status status = mTc.addUidInterfaceRules(ifIndex, data);
+ if (!isOk(status)) {
+ ALOGE("%s failed, error code = %d", __func__, status.code());
+ }
+ return (jint)status.code();
+}
+
+static jint native_removeUidInterfaceRules(JNIEnv* env, jobject clazz, jintArray jUids) {
+ ScopedIntArrayRO uids(env, jUids);
+ if (uids.get() == nullptr) {
+ return -EINVAL;
+ }
+
+ size_t size = uids.size();
+ std::vector<int32_t> data ((int32_t *)&uids[0], (int32_t*)&uids[size]);
+ Status status = mTc.removeUidInterfaceRules(data);
+ if (!isOk(status)) {
+ ALOGE("%s failed, error code = %d", __func__, status.code());
+ }
+ return (jint)status.code();
+}
+
+static jint native_swapActiveStatsMap(JNIEnv* env, jobject clazz) {
+ Status status = mTc.swapActiveStatsMap();
+ if (!isOk(status)) {
+ ALOGD("%s failed, error code = %d", __func__, status.code());
+ }
+ return (jint)status.code();
+}
+
+static void native_setPermissionForUids(JNIEnv* env, jobject clazz, jint permission,
+ jintArray jUids) {
+ ScopedIntArrayRO uids(env, jUids);
+ if (uids.get() == nullptr) return;
+
+ size_t size = uids.size();
+ static_assert(sizeof(*(uids.get())) == sizeof(uid_t));
+ std::vector<uid_t> data ((uid_t *)&uids[0], (uid_t*)&uids[size]);
+ mTc.setPermissionForUids(permission, data);
+}
+
+static jint native_setCounterSet(JNIEnv* env, jobject clazz, jint setNum, jint uid) {
+ uid_t callingUid = getuid();
+ int res = mTc.setCounterSet(setNum, (uid_t)uid, callingUid);
+ if (res) {
+ ALOGE("%s failed, error code = %d", __func__, res);
+ }
+ return (jint)res;
+}
+
+static jint native_deleteTagData(JNIEnv* env, jobject clazz, jint tagNum, jint uid) {
+ uid_t callingUid = getuid();
+ int res = mTc.deleteTagData(tagNum, (uid_t)uid, callingUid);
+ if (res) {
+ ALOGE("%s failed, error code = %d", __func__, res);
+ }
+ return (jint)res;
+}
+
+/*
+ * JNI registration.
+ */
+// clang-format off
+static const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ {"native_init", "()V",
+ (void*)native_init},
+ {"native_addNaughtyApp", "(I)I",
+ (void*)native_addNaughtyApp},
+ {"native_removeNaughtyApp", "(I)I",
+ (void*)native_removeNaughtyApp},
+ {"native_addNiceApp", "(I)I",
+ (void*)native_addNiceApp},
+ {"native_removeNiceApp", "(I)I",
+ (void*)native_removeNiceApp},
+ {"native_setChildChain", "(IZ)I",
+ (void*)native_setChildChain},
+ {"native_replaceUidChain", "(Ljava/lang/String;Z[I)I",
+ (void*)native_replaceUidChain},
+ {"native_setUidRule", "(III)I",
+ (void*)native_setUidRule},
+ {"native_addUidInterfaceRules", "(Ljava/lang/String;[I)I",
+ (void*)native_addUidInterfaceRules},
+ {"native_removeUidInterfaceRules", "([I)I",
+ (void*)native_removeUidInterfaceRules},
+ {"native_swapActiveStatsMap", "()I",
+ (void*)native_swapActiveStatsMap},
+ {"native_setPermissionForUids", "(I[I)V",
+ (void*)native_setPermissionForUids},
+ {"native_setCounterSet", "(II)I",
+ (void*)native_setCounterSet},
+ {"native_deleteTagData", "(II)I",
+ (void*)native_deleteTagData},
+};
+// clang-format on
+
+int register_com_android_server_BpfNetMaps(JNIEnv* env) {
+ return jniRegisterNativeMethods(env,
+ "com/android/server/BpfNetMaps",
+ gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/service/native/jni/onload.cpp b/service/native/jni/onload.cpp
new file mode 100644
index 0000000..df7c77b
--- /dev/null
+++ b/service/native/jni/onload.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "TrafficControllerJni"
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+
+#include "utils/Log.h"
+
+namespace android {
+
+int register_com_android_server_BpfNetMaps(JNIEnv* env);
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("%s: ERROR: GetEnv failed", __func__);
+ return JNI_ERR;
+ }
+
+ if (register_com_android_server_BpfNetMaps(env) < 0)
+ return JNI_ERR;
+
+ return JNI_VERSION_1_6;
+}
+
+}; // namespace android
diff --git a/service/native/libs/libclat/Android.bp b/service/native/libs/libclat/Android.bp
new file mode 100644
index 0000000..5e208d8
--- /dev/null
+++ b/service/native/libs/libclat/Android.bp
@@ -0,0 +1,70 @@
+// 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"],
+ header_libs: [
+ "bpf_connectivity_headers",
+ "bpf_headers",
+ "bpf_syscall_wrappers",
+ ],
+ srcs: [
+ "TcUtils.cpp", // TODO: move to frameworks/libs/net
+ "bpfhelper.cpp",
+ "clatutils.cpp",
+ ],
+ stl: "libc++_static",
+ static_libs: [
+ "libbase",
+ "libip_checksum",
+ "libnetdutils", // for netdutils/UidConstants.h in bpf_shared.h
+ ],
+ 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"],
+ header_libs: [
+ "bpf_connectivity_headers",
+ "bpf_headers",
+ "bpf_syscall_wrappers",
+ ],
+ srcs: [
+ "TcUtilsTest.cpp",
+ "clatutils_test.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libclat",
+ "libip_checksum",
+ "libnetd_test_tun_interface",
+ "libnetdutils", // for netdutils/UidConstants.h in bpf_shared.h
+ "libtcutils",
+ ],
+ shared_libs: [
+ "liblog",
+ "libnetutils",
+ ],
+ require_root: true,
+}
diff --git a/service/native/libs/libclat/TcUtils.cpp b/service/native/libs/libclat/TcUtils.cpp
new file mode 100644
index 0000000..cdfb763
--- /dev/null
+++ b/service/native/libs/libclat/TcUtils.cpp
@@ -0,0 +1,390 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TcUtils"
+
+#include "libclat/TcUtils.h"
+
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/netlink.h>
+#include <linux/pkt_cls.h>
+#include <linux/pkt_sched.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace net {
+
+using std::max;
+
+// Sync from system/netd/server/NetlinkCommands.h
+const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
+const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
+
+static int doSIOCGIF(const std::string& interface, int opt) {
+ base::unique_fd ufd(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+
+ if (ufd < 0) {
+ const int err = errno;
+ ALOGE("socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)");
+ return -err;
+ };
+
+ 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.c_str(), sizeof(ifr.ifr_name));
+
+ if (ioctl(ufd, opt, &ifr, sizeof(ifr))) return -errno;
+
+ if (opt == SIOCGIFHWADDR) return ifr.ifr_hwaddr.sa_family;
+ if (opt == SIOCGIFMTU) return ifr.ifr_mtu;
+ return -EINVAL;
+}
+
+int hardwareAddressType(const std::string& interface) {
+ return doSIOCGIF(interface, SIOCGIFHWADDR);
+}
+
+int deviceMTU(const std::string& interface) {
+ return doSIOCGIF(interface, SIOCGIFMTU);
+}
+
+base::Result<bool> isEthernet(const std::string& interface) {
+ int rv = hardwareAddressType(interface);
+ if (rv < 0) {
+ errno = -rv;
+ return ErrnoErrorf("Get hardware address type of interface {} failed", interface);
+ }
+
+ 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:
+ errno = EAFNOSUPPORT; // Address family not supported
+ return ErrnoErrorf("Unknown hardware address type {} on interface {}", rv, interface);
+ }
+}
+
+// TODO: use //system/netd/server/NetlinkCommands.cpp:openNetlinkSocket(protocol)
+// and //system/netd/server/SockDiag.cpp:checkError(fd)
+static int sendAndProcessNetlinkResponse(const void* req, int len) {
+ base::unique_fd fd(socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE));
+ if (fd == -1) {
+ const int err = errno;
+ ALOGE("socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE)");
+ return -err;
+ }
+
+ static constexpr int on = 1;
+ int rv = setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on));
+ if (rv) ALOGE("setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, %d)", on);
+
+ // this is needed to get sane strace netlink parsing, it allocates the pid
+ rv = bind(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR));
+ if (rv) {
+ const int err = errno;
+ ALOGE("bind(fd, {AF_NETLINK, 0, 0})");
+ return -err;
+ }
+
+ // we do not want to receive messages from anyone besides the kernel
+ rv = connect(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR));
+ if (rv) {
+ const int err = errno;
+ ALOGE("connect(fd, {AF_NETLINK, 0, 0})");
+ return -err;
+ }
+
+ rv = send(fd, req, len, 0);
+ if (rv == -1) return -errno;
+ if (rv != len) return -EMSGSIZE;
+
+ struct {
+ nlmsghdr h;
+ nlmsgerr e;
+ char buf[256];
+ } resp = {};
+
+ rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
+
+ if (rv == -1) {
+ const int err = errno;
+ ALOGE("recv() failed");
+ return -err;
+ }
+
+ if (rv < (int)NLMSG_SPACE(sizeof(struct nlmsgerr))) {
+ ALOGE("recv() returned short packet: %d", rv);
+ return -EMSGSIZE;
+ }
+
+ if (resp.h.nlmsg_len != (unsigned)rv) {
+ ALOGE("recv() returned invalid header length: %d != %d", resp.h.nlmsg_len, rv);
+ return -EBADMSG;
+ }
+
+ if (resp.h.nlmsg_type != NLMSG_ERROR) {
+ ALOGE("recv() did not return NLMSG_ERROR message: %d", resp.h.nlmsg_type);
+ return -EBADMSG;
+ }
+
+ return resp.e.error; // returns 0 on success
+}
+
+// ADD: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_EXCL|NLM_F_CREATE
+// REPLACE: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_CREATE|NLM_F_REPLACE
+// DEL: nlMsgType=RTM_DELQDISC nlMsgFlags=0
+int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags) {
+ // This is the name of the qdisc we are attaching.
+ // Some hoop jumping to make this compile time constant with known size,
+ // so that the structure declaration is well defined at compile time.
+#define CLSACT "clsact"
+ // sizeof() includes the terminating NULL
+ static constexpr size_t ASCIIZ_LEN_CLSACT = sizeof(CLSACT);
+
+ const struct {
+ nlmsghdr n;
+ tcmsg t;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(ASCIIZ_LEN_CLSACT)];
+ } kind;
+ } req = {
+ .n =
+ {
+ .nlmsg_len = sizeof(req),
+ .nlmsg_type = nlMsgType,
+ .nlmsg_flags = static_cast<__u16>(NETLINK_REQUEST_FLAGS | nlMsgFlags),
+ },
+ .t =
+ {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = ifIndex,
+ .tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0),
+ .tcm_parent = TC_H_CLSACT,
+ },
+ .kind =
+ {
+ .attr =
+ {
+ .nla_len = NLA_HDRLEN + ASCIIZ_LEN_CLSACT,
+ .nla_type = TCA_KIND,
+ },
+ .str = CLSACT,
+ },
+ };
+#undef CLSACT
+
+ return sendAndProcessNetlinkResponse(&req, sizeof(req));
+}
+
+// tc filter add dev .. in/egress prio 4 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
+// direct-action
+int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t proto, int bpfFd, bool ethernet) {
+ // This is the name of the filter we're attaching (ie. this is the 'bpf'
+ // packet classifier enabled by kernel config option CONFIG_NET_CLS_BPF.
+ //
+ // We go through some hoops in order to make this compile time constants
+ // so that we can define the struct further down the function with the
+ // field for this sized correctly already during the build.
+#define BPF "bpf"
+ // sizeof() includes the terminating NULL
+ static constexpr size_t ASCIIZ_LEN_BPF = sizeof(BPF);
+
+ // This is to replicate program name suffix used by 'tc' Linux cli
+ // when it attaches programs.
+#define FSOBJ_SUFFIX ":[*fsobj]"
+
+ // This macro expands (from header files) to:
+ // prog_clatd_schedcls_ingress6_clat_rawip:[*fsobj]
+ // and is the name of the pinned ingress ebpf program for ARPHRD_RAWIP interfaces.
+ // (also compatible with anything that has 0 size L2 header)
+ static constexpr char name_clat_rx_rawip[] = CLAT_INGRESS6_PROG_RAWIP_NAME FSOBJ_SUFFIX;
+
+ // This macro expands (from header files) to:
+ // prog_clatd_schedcls_ingress6_clat_ether:[*fsobj]
+ // and is the name of the pinned ingress ebpf program for ARPHRD_ETHER interfaces.
+ // (also compatible with anything that has standard ethernet header)
+ static constexpr char name_clat_rx_ether[] = CLAT_INGRESS6_PROG_ETHER_NAME FSOBJ_SUFFIX;
+
+ // This macro expands (from header files) to:
+ // prog_clatd_schedcls_egress4_clat_rawip:[*fsobj]
+ // and is the name of the pinned egress ebpf program for ARPHRD_RAWIP interfaces.
+ // (also compatible with anything that has 0 size L2 header)
+ static constexpr char name_clat_tx_rawip[] = CLAT_EGRESS4_PROG_RAWIP_NAME FSOBJ_SUFFIX;
+
+ // This macro expands (from header files) to:
+ // prog_clatd_schedcls_egress4_clat_ether:[*fsobj]
+ // and is the name of the pinned egress ebpf program for ARPHRD_ETHER interfaces.
+ // (also compatible with anything that has standard ethernet header)
+ static constexpr char name_clat_tx_ether[] = CLAT_EGRESS4_PROG_ETHER_NAME FSOBJ_SUFFIX;
+
+#undef FSOBJ_SUFFIX
+
+ // The actual name we'll use is determined at run time via 'ethernet' and 'ingress'
+ // booleans. We need to compile time allocate enough space in the struct
+ // hence this macro magic to make sure we have enough space for either
+ // possibility. In practice some of these are actually the same size.
+ static constexpr size_t ASCIIZ_MAXLEN_NAME = max({
+ sizeof(name_clat_rx_rawip),
+ sizeof(name_clat_rx_ether),
+ sizeof(name_clat_tx_rawip),
+ sizeof(name_clat_tx_ether),
+ });
+
+ // These are not compile time constants: 'name' is used in strncpy below
+ const char* const name_clat_rx = ethernet ? name_clat_rx_ether : name_clat_rx_rawip;
+ const char* const name_clat_tx = ethernet ? name_clat_tx_ether : name_clat_tx_rawip;
+ const char* const name = ingress ? name_clat_rx : name_clat_tx;
+
+ struct {
+ nlmsghdr n;
+ tcmsg t;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(ASCIIZ_LEN_BPF)];
+ } kind;
+ struct {
+ nlattr attr;
+ struct {
+ nlattr attr;
+ __u32 u32;
+ } fd;
+ struct {
+ nlattr attr;
+ char str[NLMSG_ALIGN(ASCIIZ_MAXLEN_NAME)];
+ } 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>((PRIO_CLAT << 16) | htons(proto)),
+ },
+ .kind =
+ {
+ .attr =
+ {
+ .nla_len = sizeof(req.kind),
+ .nla_type = TCA_KIND,
+ },
+ .str = BPF,
+ },
+ .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,
+ },
+ },
+ };
+#undef BPF
+
+ strncpy(req.options.name.str, name, sizeof(req.options.name.str));
+
+ return sendAndProcessNetlinkResponse(&req, sizeof(req));
+}
+
+// tc filter del dev .. in/egress prio 4 protocol ..
+int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t 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<uint32_t>(prio) << 16) |
+ static_cast<uint32_t>(htons(proto)),
+ },
+ };
+
+ return sendAndProcessNetlinkResponse(&req, sizeof(req));
+}
+
+} // namespace net
+} // namespace android
diff --git a/service/native/libs/libclat/TcUtilsTest.cpp b/service/native/libs/libclat/TcUtilsTest.cpp
new file mode 100644
index 0000000..08f3042
--- /dev/null
+++ b/service/native/libs/libclat/TcUtilsTest.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * TcUtilsTest.cpp - unit tests for TcUtils.cpp
+ */
+
+#include <gtest/gtest.h>
+
+#include "libclat/TcUtils.h"
+
+#include <linux/if_arp.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "bpf/BpfUtils.h"
+#include "bpf_shared.h"
+
+namespace android {
+namespace net {
+
+class TcUtilsTest : public ::testing::Test {
+ public:
+ void SetUp() {}
+};
+
+TEST_F(TcUtilsTest, HardwareAddressTypeOfNonExistingIf) {
+ ASSERT_EQ(-ENODEV, hardwareAddressType("not_existing_if"));
+}
+
+TEST_F(TcUtilsTest, HardwareAddressTypeOfLoopback) {
+ ASSERT_EQ(ARPHRD_LOOPBACK, hardwareAddressType("lo"));
+}
+
+// If wireless 'wlan0' interface exists it should be Ethernet.
+TEST_F(TcUtilsTest, HardwareAddressTypeOfWireless) {
+ int type = hardwareAddressType("wlan0");
+ if (type == -ENODEV) return;
+
+ ASSERT_EQ(ARPHRD_ETHER, type);
+}
+
+// If cellular 'rmnet_data0' interface exists it should
+// *probably* not be Ethernet and instead be RawIp.
+TEST_F(TcUtilsTest, HardwareAddressTypeOfCellular) {
+ int type = hardwareAddressType("rmnet_data0");
+ if (type == -ENODEV) return;
+
+ ASSERT_NE(ARPHRD_ETHER, type);
+
+ // ARPHRD_RAWIP is 530 on some pre-4.14 Qualcomm devices.
+ if (type == 530) return;
+
+ ASSERT_EQ(ARPHRD_RAWIP, type);
+}
+
+TEST_F(TcUtilsTest, IsEthernetOfNonExistingIf) {
+ auto res = isEthernet("not_existing_if");
+ ASSERT_FALSE(res.ok());
+ ASSERT_EQ(ENODEV, res.error().code());
+}
+
+TEST_F(TcUtilsTest, IsEthernetOfLoopback) {
+ auto res = isEthernet("lo");
+ ASSERT_FALSE(res.ok());
+ ASSERT_EQ(EAFNOSUPPORT, res.error().code());
+}
+
+// If wireless 'wlan0' interface exists it should be Ethernet.
+// See also HardwareAddressTypeOfWireless.
+TEST_F(TcUtilsTest, IsEthernetOfWireless) {
+ auto res = isEthernet("wlan0");
+ if (!res.ok() && res.error().code() == ENODEV) return;
+
+ ASSERT_RESULT_OK(res);
+ ASSERT_TRUE(res.value());
+}
+
+// If cellular 'rmnet_data0' interface exists it should
+// *probably* not be Ethernet and instead be RawIp.
+// See also HardwareAddressTypeOfCellular.
+TEST_F(TcUtilsTest, IsEthernetOfCellular) {
+ auto res = isEthernet("rmnet_data0");
+ if (!res.ok() && res.error().code() == ENODEV) return;
+
+ ASSERT_RESULT_OK(res);
+ ASSERT_FALSE(res.value());
+}
+
+TEST_F(TcUtilsTest, DeviceMTUOfNonExistingIf) {
+ ASSERT_EQ(-ENODEV, deviceMTU("not_existing_if"));
+}
+
+TEST_F(TcUtilsTest, DeviceMTUofLoopback) {
+ ASSERT_EQ(65536, deviceMTU("lo"));
+}
+
+TEST_F(TcUtilsTest, GetClatEgress4MapFd) {
+ int fd = getClatEgress4MapFd();
+ ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(TcUtilsTest, GetClatEgress4RawIpProgFd) {
+ int fd = getClatEgress4ProgFd(RAWIP);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(TcUtilsTest, GetClatEgress4EtherProgFd) {
+ int fd = getClatEgress4ProgFd(ETHER);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(TcUtilsTest, GetClatIngress6MapFd) {
+ int fd = getClatIngress6MapFd();
+ ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(TcUtilsTest, GetClatIngress6RawIpProgFd) {
+ int fd = getClatIngress6ProgFd(RAWIP);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+TEST_F(TcUtilsTest, GetClatIngress6EtherProgFd) {
+ int fd = getClatIngress6ProgFd(ETHER);
+ ASSERT_GE(fd, 3);
+ EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
+ close(fd);
+}
+
+// See Linux kernel source in include/net/flow.h
+#define LOOPBACK_IFINDEX 1
+
+TEST_F(TcUtilsTest, AttachReplaceDetachClsactLo) {
+ // This attaches and detaches a configuration-less and thus no-op clsact
+ // qdisc to loopback interface (and it takes fractions of a second)
+ EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscReplaceDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
+}
+
+static void checkAttachDetachBpfFilterClsactLo(const bool ingress, const bool ethernet) {
+ // Older kernels return EINVAL instead of ENOENT due to lacking proper error propagation...
+ const int errNOENT = android::bpf::isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
+
+ int clatBpfFd = ingress ? getClatIngress6ProgFd(ethernet) : getClatEgress4ProgFd(ethernet);
+ ASSERT_GE(clatBpfFd, 3);
+
+ // This attaches and detaches a clsact plus ebpf program to loopback
+ // interface, but it should not affect traffic by virtue of us not
+ // actually populating the ebpf control map.
+ // Furthermore: it only takes fractions of a second.
+ EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-errNOENT, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+ if (ingress) {
+ EXPECT_EQ(0, tcFilterAddDevIngressClatIpv6(LOOPBACK_IFINDEX, clatBpfFd, ethernet));
+ EXPECT_EQ(0, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ } else {
+ EXPECT_EQ(0, tcFilterAddDevEgressClatIpv4(LOOPBACK_IFINDEX, clatBpfFd, ethernet));
+ EXPECT_EQ(0, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+ }
+ EXPECT_EQ(-errNOENT, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+ EXPECT_EQ(0, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
+ EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
+
+ close(clatBpfFd);
+}
+
+TEST_F(TcUtilsTest, CheckAttachBpfFilterRawIpClsactEgressLo) {
+ checkAttachDetachBpfFilterClsactLo(EGRESS, RAWIP);
+}
+
+TEST_F(TcUtilsTest, CheckAttachBpfFilterEthernetClsactEgressLo) {
+ checkAttachDetachBpfFilterClsactLo(EGRESS, ETHER);
+}
+
+TEST_F(TcUtilsTest, CheckAttachBpfFilterRawIpClsactIngressLo) {
+ checkAttachDetachBpfFilterClsactLo(INGRESS, RAWIP);
+}
+
+TEST_F(TcUtilsTest, CheckAttachBpfFilterEthernetClsactIngressLo) {
+ checkAttachDetachBpfFilterClsactLo(INGRESS, ETHER);
+}
+
+} // namespace net
+} // namespace android
diff --git a/service/native/libs/libclat/bpfhelper.cpp b/service/native/libs/libclat/bpfhelper.cpp
new file mode 100644
index 0000000..6e230d0
--- /dev/null
+++ b/service/native/libs/libclat/bpfhelper.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * main.c - main function
+ */
+#define LOG_TAG "bpfhelper"
+
+#include "libclat/bpfhelper.h"
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+
+#include "bpf/BpfMap.h"
+#include "libclat/TcUtils.h"
+
+#define DEVICEPREFIX "v4-"
+
+using android::base::unique_fd;
+using android::net::RAWIP;
+using android::net::getClatEgress4MapFd;
+using android::net::getClatIngress6MapFd;
+using android::net::getClatEgress4ProgFd;
+using android::net::getClatIngress6ProgFd;
+using android::net::tcQdiscAddDevClsact;
+using android::net::tcFilterAddDevEgressClatIpv4;
+using android::net::tcFilterAddDevIngressClatIpv6;
+using android::net::tcFilterDelDevEgressClatIpv4;
+using android::net::tcFilterDelDevIngressClatIpv6;
+using android::bpf::BpfMap;
+
+BpfMap<ClatEgress4Key, ClatEgress4Value> mClatEgress4Map;
+BpfMap<ClatIngress6Key, ClatIngress6Value> mClatIngress6Map;
+
+namespace android {
+namespace net {
+namespace clat {
+
+// TODO: have a clearMap function to remove all stubs while system server crash.
+// For long term, move bpf access into java and map initialization should live
+// ClatCoordinator constructor.
+int initMaps(void) {
+ int rv = getClatEgress4MapFd();
+ if (rv < 0) {
+ ALOGE("getClatEgress4MapFd() failure: %s", strerror(-rv));
+ return -rv;
+ }
+ mClatEgress4Map.reset(rv);
+
+ rv = getClatIngress6MapFd();
+ if (rv < 0) {
+ ALOGE("getClatIngress6MapFd() failure: %s", strerror(-rv));
+ mClatEgress4Map.reset(-1);
+ return -rv;
+ }
+ mClatIngress6Map.reset(rv);
+
+ return 0;
+}
+
+void maybeStartBpf(const ClatdTracker& tracker) {
+ auto isEthernet = android::net::isEthernet(tracker.iface);
+ if (!isEthernet.ok()) {
+ ALOGE("isEthernet(%s[%d]) failure: %s", tracker.iface, tracker.ifIndex,
+ isEthernet.error().message().c_str());
+ return;
+ }
+
+ // This program will be attached to the v4-* interface which is a TUN and thus always rawip.
+ int rv = getClatEgress4ProgFd(RAWIP);
+ if (rv < 0) {
+ ALOGE("getClatEgress4ProgFd(RAWIP) failure: %s", strerror(-rv));
+ return;
+ }
+ unique_fd txRawIpProgFd(rv);
+
+ rv = getClatIngress6ProgFd(isEthernet.value());
+ if (rv < 0) {
+ ALOGE("getClatIngress6ProgFd(%d) failure: %s", isEthernet.value(), strerror(-rv));
+ return;
+ }
+ unique_fd rxProgFd(rv);
+
+ ClatEgress4Key txKey = {
+ .iif = tracker.v4ifIndex,
+ .local4 = tracker.v4,
+ };
+ ClatEgress4Value txValue = {
+ .oif = tracker.ifIndex,
+ .local6 = tracker.v6,
+ .pfx96 = tracker.pfx96,
+ .oifIsEthernet = isEthernet.value(),
+ };
+
+ auto ret = mClatEgress4Map.writeValue(txKey, txValue, BPF_ANY);
+ if (!ret.ok()) {
+ ALOGE("mClatEgress4Map.writeValue failure: %s", strerror(ret.error().code()));
+ return;
+ }
+
+ ClatIngress6Key rxKey = {
+ .iif = tracker.ifIndex,
+ .pfx96 = tracker.pfx96,
+ .local6 = tracker.v6,
+ };
+ ClatIngress6Value rxValue = {
+ // TODO: move all the clat code to eBPF and remove the tun interface entirely.
+ .oif = tracker.v4ifIndex,
+ .local4 = tracker.v4,
+ };
+
+ ret = mClatIngress6Map.writeValue(rxKey, rxValue, BPF_ANY);
+ if (!ret.ok()) {
+ ALOGE("mClatIngress6Map.writeValue failure: %s", strerror(ret.error().code()));
+ ret = mClatEgress4Map.deleteValue(txKey);
+ if (!ret.ok())
+ ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
+ return;
+ }
+
+ // We do tc setup *after* populating the maps, so scanning through them
+ // can always be used to tell us what needs cleanup.
+
+ // Usually the clsact will be added in RouteController::addInterfaceToPhysicalNetwork.
+ // But clat is started before the v4- interface is added to the network. The clat startup have
+ // to add clsact of v4- tun interface first for adding bpf filter in maybeStartBpf.
+ // TODO: move "qdisc add clsact" of v4- tun interface out from ClatdController.
+ rv = tcQdiscAddDevClsact(tracker.v4ifIndex);
+ if (rv) {
+ ALOGE("tcQdiscAddDevClsact(%d[%s]) failure: %s", tracker.v4ifIndex, tracker.v4iface,
+ strerror(-rv));
+ ret = mClatEgress4Map.deleteValue(txKey);
+ if (!ret.ok())
+ ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
+ ret = mClatIngress6Map.deleteValue(rxKey);
+ if (!ret.ok())
+ ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
+ return;
+ }
+
+ rv = tcFilterAddDevEgressClatIpv4(tracker.v4ifIndex, txRawIpProgFd, RAWIP);
+ if (rv) {
+ ALOGE("tcFilterAddDevEgressClatIpv4(%d[%s], RAWIP) failure: %s", tracker.v4ifIndex,
+ tracker.v4iface, strerror(-rv));
+
+ // The v4- interface clsact is not deleted for unwinding error because once it is created
+ // with interface addition, the lifetime is till interface deletion. Moreover, the clsact
+ // has no clat filter now. It should not break anything.
+
+ ret = mClatEgress4Map.deleteValue(txKey);
+ if (!ret.ok())
+ ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
+ ret = mClatIngress6Map.deleteValue(rxKey);
+ if (!ret.ok())
+ ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
+ return;
+ }
+
+ rv = tcFilterAddDevIngressClatIpv6(tracker.ifIndex, rxProgFd, isEthernet.value());
+ if (rv) {
+ ALOGE("tcFilterAddDevIngressClatIpv6(%d[%s], %d) failure: %s", tracker.ifIndex,
+ tracker.iface, isEthernet.value(), strerror(-rv));
+ rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex);
+ if (rv) {
+ ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex,
+ tracker.v4iface, strerror(-rv));
+ }
+
+ // The v4- interface clsact is not deleted. See the reason in the error unwinding code of
+ // the egress filter attaching of v4- tun interface.
+
+ ret = mClatEgress4Map.deleteValue(txKey);
+ if (!ret.ok())
+ ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
+ ret = mClatIngress6Map.deleteValue(rxKey);
+ if (!ret.ok())
+ ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
+ return;
+ }
+
+ // success
+}
+
+void maybeStopBpf(const ClatdTracker& tracker) {
+ int rv = tcFilterDelDevIngressClatIpv6(tracker.ifIndex);
+ if (rv < 0) {
+ ALOGE("tcFilterDelDevIngressClatIpv6(%d[%s]) failure: %s", tracker.ifIndex, tracker.iface,
+ strerror(-rv));
+ }
+
+ rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex);
+ if (rv < 0) {
+ ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex,
+ tracker.v4iface, strerror(-rv));
+ }
+
+ // We cleanup the maps last, so scanning through them can be used to
+ // determine what still needs cleanup.
+
+ ClatEgress4Key txKey = {
+ .iif = tracker.v4ifIndex,
+ .local4 = tracker.v4,
+ };
+
+ auto ret = mClatEgress4Map.deleteValue(txKey);
+ if (!ret.ok()) ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
+
+ ClatIngress6Key rxKey = {
+ .iif = tracker.ifIndex,
+ .pfx96 = tracker.pfx96,
+ .local6 = tracker.v6,
+ };
+
+ ret = mClatIngress6Map.deleteValue(rxKey);
+ if (!ret.ok()) ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
+}
+
+} // namespace clat
+} // namespace net
+} // namespace android
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/TcUtils.h b/service/native/libs/libclat/include/libclat/TcUtils.h
new file mode 100644
index 0000000..212838e
--- /dev/null
+++ b/service/native/libs/libclat/include/libclat/TcUtils.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+#include <errno.h>
+#include <linux/if_ether.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+#include <string>
+
+#include "bpf/BpfUtils.h"
+#include "bpf_shared.h"
+
+namespace android {
+namespace net {
+
+// For better code clarity - do not change values - used for booleans like
+// with_ethernet_header or isEthernet.
+constexpr bool RAWIP = false;
+constexpr bool ETHER = true;
+
+// For better code clarity when used for 'bool ingress' parameter.
+constexpr bool EGRESS = false;
+constexpr bool INGRESS = true;
+
+// The priority of clat hook - must be after tethering.
+constexpr uint16_t PRIO_CLAT = 4;
+
+// this returns an ARPHRD_* constant or a -errno
+int hardwareAddressType(const std::string& interface);
+
+// return MTU or -errno
+int deviceMTU(const std::string& interface);
+
+base::Result<bool> isEthernet(const std::string& interface);
+
+inline int getClatEgress4MapFd(void) {
+ const int fd = bpf::mapRetrieveRW(CLAT_EGRESS4_MAP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getClatEgress4ProgFd(bool with_ethernet_header) {
+ const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_EGRESS4_PROG_ETHER_PATH
+ : CLAT_EGRESS4_PROG_RAWIP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getClatIngress6MapFd(void) {
+ const int fd = bpf::mapRetrieveRW(CLAT_INGRESS6_MAP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+inline int getClatIngress6ProgFd(bool with_ethernet_header) {
+ const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_INGRESS6_PROG_ETHER_PATH
+ : CLAT_INGRESS6_PROG_RAWIP_PATH);
+ return (fd == -1) ? -errno : fd;
+}
+
+int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags);
+
+inline int tcQdiscAddDevClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE);
+}
+
+inline int tcQdiscReplaceDevClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE);
+}
+
+inline int tcQdiscDelDevClsact(int ifIndex) {
+ return doTcQdiscClsact(ifIndex, RTM_DELQDISC, 0);
+}
+
+// tc filter add dev .. in/egress prio 4 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
+// direct-action
+int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t proto, int bpfFd, bool ethernet);
+
+// tc filter add dev .. ingress prio 4 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action
+inline int tcFilterAddDevIngressClatIpv6(int ifIndex, int bpfFd, bool ethernet) {
+ return tcFilterAddDevBpf(ifIndex, INGRESS, ETH_P_IPV6, bpfFd, ethernet);
+}
+
+// tc filter add dev .. egress prio 4 protocol ip bpf object-pinned /sys/fs/bpf/... direct-action
+inline int tcFilterAddDevEgressClatIpv4(int ifIndex, int bpfFd, bool ethernet) {
+ return tcFilterAddDevBpf(ifIndex, EGRESS, ETH_P_IP, bpfFd, ethernet);
+}
+
+// tc filter del dev .. in/egress prio .. protocol ..
+int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t proto);
+
+// tc filter del dev .. ingress prio 4 protocol ipv6
+inline int tcFilterDelDevIngressClatIpv6(int ifIndex) {
+ return tcFilterDelDev(ifIndex, INGRESS, PRIO_CLAT, ETH_P_IPV6);
+}
+
+// tc filter del dev .. egress prio 4 protocol ip
+inline int tcFilterDelDevEgressClatIpv4(int ifIndex) {
+ return tcFilterDelDev(ifIndex, EGRESS, PRIO_CLAT, ETH_P_IP);
+}
+
+} // namespace net
+} // namespace android
diff --git a/service/native/libs/libclat/include/libclat/bpfhelper.h b/service/native/libs/libclat/include/libclat/bpfhelper.h
new file mode 100644
index 0000000..c0328c0
--- /dev/null
+++ b/service/native/libs/libclat/include/libclat/bpfhelper.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <arpa/inet.h>
+#include <linux/if.h>
+
+namespace android {
+namespace net {
+namespace clat {
+
+struct ClatdTracker {
+ unsigned ifIndex;
+ char iface[IFNAMSIZ];
+ unsigned v4ifIndex;
+ char v4iface[IFNAMSIZ];
+ in_addr v4;
+ in6_addr v6;
+ in6_addr pfx96;
+};
+
+int initMaps(void);
+void maybeStartBpf(const ClatdTracker& tracker);
+void maybeStopBpf(const ClatdTracker& tracker);
+
+} // 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..a6909c0
--- /dev/null
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -0,0 +1,261 @@
+/*
+ * 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";
+
+ static {
+ System.loadLibrary("traffic_controller_jni");
+ native_init();
+ }
+
+ /**
+ * 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 static native void native_init();
+ 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 a59b5d6..ae6fe11 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -73,6 +73,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_5;
import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
@@ -166,6 +168,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;
@@ -258,9 +261,10 @@
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;
@@ -1488,16 +1492,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;
}
@@ -4041,11 +4046,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());
@@ -5046,9 +5051,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;
}
@@ -5671,7 +5677,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);
@@ -6605,7 +6612,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.
@@ -10103,19 +10111,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 ("
@@ -10126,23 +10141,87 @@
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;
+ if (preference.getPreferenceEnterpriseId() != 0) {
+ throw new IllegalArgumentException(
+ "Invalid enterprise identifier in setProfileNetworkPreferences");
+ }
+ break;
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK:
+ allowFallback = false;
+ // continue to process the enterprise preference.
+ case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
+ if (!isEnterpriseIdentifierValid(preference.getPreferenceEnterpriseId())) {
+ throw new IllegalArgumentException(
+ "Invalid enterprise identifier in setProfileNetworkPreferences");
+ }
+ 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.addEnterpriseId(
+ preference.getPreferenceEnterpriseId());
+ 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 boolean isEnterpriseIdentifierValid(
+ @NetworkCapabilities.EnterpriseId int identifier) {
+ if ((identifier >= NET_ENTERPRISE_ID_1)
+ && (identifier <= NET_ENTERPRISE_ID_5)) {
+ return true;
+ }
+ return false;
}
private void validateNetworkCapabilitiesOfProfileNetworkPreference(
@@ -10152,20 +10231,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);
@@ -10174,12 +10258,32 @@
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);
+ 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));
@@ -10562,4 +10666,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..5a5e24a
--- /dev/null
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -0,0 +1,380 @@
+/*
+ * 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;
+ @Nullable
+ private String mNat64Prefix = null;
+ @Nullable
+ private String mXlatLocalAddress4 = null;
+ @Nullable
+ private String mXlatLocalAddress6 = 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);
+ }
+
+ /**
+ * Maybe start bpf.
+ */
+ public int maybeStartBpf(@NonNull FileDescriptor tunfd, @NonNull FileDescriptor readsock6,
+ @NonNull FileDescriptor writesock6, @NonNull String iface, @NonNull String pfx96,
+ @NonNull String v4, @NonNull String v6) throws IOException {
+ return native_maybeStartBpf(tunfd, readsock6, writesock6, iface, pfx96, v4, v6);
+ }
+
+ /**
+ * Maybe stop bpf.
+ */
+ public void maybeStopBpf(String iface, String pfx96, String v4, String v6, int pid)
+ throws IOException {
+ native_maybeStopBpf(iface, pfx96, v4, v6, pid);
+ }
+ }
+
+ @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);
+ }
+
+ // [5] Maybe start bpf.
+ try {
+ mDeps.maybeStartBpf(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(),
+ writeSock6.getFileDescriptor(), iface, pfx96, v4, v6);
+ mIface = iface;
+ mNat64Prefix = pfx96;
+ mXlatLocalAddress4 = v4;
+ mXlatLocalAddress6 = v6;
+ } catch (IOException e) {
+ throw new IOException("Error start bpf on " + iface + ": " + e);
+ }
+
+ // TODO: start clatd and returns local xlat464 v6 address.
+ return null;
+ }
+
+ /**
+ * Stop clatd
+ */
+ public void clatStop() throws IOException {
+ mDeps.maybeStopBpf(mIface, mNat64Prefix, mXlatLocalAddress4, mXlatLocalAddress6,
+ mPid /* unused */);
+ // TODO: remove setIptablesDropRule
+
+ Log.i(TAG, "clatd on " + mIface + " stopped");
+
+ mIface = null;
+ mNat64Prefix = null;
+ mXlatLocalAddress4 = null;
+ mXlatLocalAddress6 = 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;
+ private static native int native_maybeStartBpf(FileDescriptor tunfd, FileDescriptor readsock6,
+ FileDescriptor writesock6, String iface, String pfx96, String v4, String v6)
+ throws IOException;
+ private static native void native_maybeStopBpf(String iface, String pfx96, String v4,
+ String v6, int pid) throws IOException;
+}
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/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 c533dab..acf04bf 100644
--- a/tests/common/Android.bp
+++ b/tests/common/Android.bp
@@ -114,6 +114,7 @@
// meaning @hide APIs in framework-connectivity are resolved before @SystemApi
// stubs in framework
"framework-connectivity.impl",
+ "framework-connectivity-tiramisu.impl",
"framework-tethering.impl",
"framework",
@@ -121,3 +122,25 @@
"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 f61c8c3..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) {
@@ -371,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 ed9995c..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,20 +50,7 @@
setBypassableVpn(true)
}
}.build()
- // This test can be run as unit test against the latest system image, as CTS to verify
- // an Android release that is as recent as the test, or as MTS to verify the
- // Connectivity module. In the first two cases NetworkAgentConfig will be as recent
- // as the test. In the last case, starting from S NetworkAgentConfig is updated as part
- // of Connectivity, so it is also as recent as the test. For MTS on Q and R,
- // NetworkAgentConfig is not updatable, so it may have a different number of fields.
- if (isAtLeastS()) {
- // When this test is run on S+, NetworkAgentConfig is as recent as the test,
- // so this should be the most recent known number of fields.
- assertParcelSane(config, 13)
- } 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 2a4df7a..d03629d 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -23,11 +23,6 @@
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;
@@ -42,6 +37,11 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_2;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_3;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_4;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_5;
import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
@@ -57,9 +57,9 @@
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;
@@ -344,21 +344,7 @@
}
private void testParcelSane(NetworkCapabilities cap) {
- // This test can be run as unit test against the latest system image, as CTS to verify
- // an Android release that is as recent as the test, or as MTS to verify the
- // Connectivity module. In the first two cases NetworkCapabilities will be as recent
- // as the test. In the last case, starting from S NetworkCapabilities is updated as part
- // of Connectivity, so it is also as recent as the test. For MTS on Q and R,
- // NetworkCapabilities is not updatable, so it may have a different number of fields.
- if (isAtLeastS()) {
- // When this test is run on S+, NetworkCapabilities is as recent as the test,
- // so this should be the most recent known number of fields.
- assertParcelSane(cap, 18);
- } else if (isAtLeastR()) {
- assertParcelSane(cap, 15);
- } else {
- assertParcelSane(cap, 11);
- }
+ assertParcelingIsLossless(cap);
}
private static NetworkCapabilities createNetworkCapabilitiesWithTransportInfo() {
@@ -803,79 +789,80 @@
} catch (IllegalStateException expected) { }
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.S)
- public void testEnterpriseCapabilitySubLevel() {
+ @Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+ public void testEnterpriseId() {
final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
.addCapability(NET_CAPABILITY_ENTERPRISE)
- .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .addEnterpriseId(NET_ENTERPRISE_ID_1)
.build();
- assertEquals(1, nc1.getEnterpriseCapabilitySubLevels().length);
- assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
- nc1.getEnterpriseCapabilitySubLevels()[0]);
+ assertEquals(1, nc1.getEnterpriseIds().length);
+ assertEquals(NET_ENTERPRISE_ID_1,
+ nc1.getEnterpriseIds()[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)
+ .addEnterpriseId(NET_ENTERPRISE_ID_1)
+ .addEnterpriseId(NET_ENTERPRISE_ID_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]);
+ assertEquals(2, nc2.getEnterpriseIds().length);
+ assertEquals(NET_ENTERPRISE_ID_1,
+ nc2.getEnterpriseIds()[0]);
+ assertEquals(NET_ENTERPRISE_ID_2,
+ nc2.getEnterpriseIds()[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)
+ .addEnterpriseId(NET_ENTERPRISE_ID_1)
+ .addEnterpriseId(NET_ENTERPRISE_ID_2)
+ .addEnterpriseId(NET_ENTERPRISE_ID_3)
+ .addEnterpriseId(NET_ENTERPRISE_ID_4)
+ .addEnterpriseId(NET_ENTERPRISE_ID_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]);
+ assertEquals(5, nc3.getEnterpriseIds().length);
+ assertEquals(NET_ENTERPRISE_ID_1,
+ nc3.getEnterpriseIds()[0]);
+ assertEquals(NET_ENTERPRISE_ID_2,
+ nc3.getEnterpriseIds()[1]);
+ assertEquals(NET_ENTERPRISE_ID_3,
+ nc3.getEnterpriseIds()[2]);
+ assertEquals(NET_ENTERPRISE_ID_4,
+ nc3.getEnterpriseIds()[3]);
+ assertEquals(NET_ENTERPRISE_ID_5,
+ nc3.getEnterpriseIds()[4]);
final Class<IllegalArgumentException> illegalArgumentExceptionClass =
IllegalArgumentException.class;
assertThrows(illegalArgumentExceptionClass, () -> new NetworkCapabilities.Builder()
- .addEnterpriseCapabilitySubLevel(6)
+ .addEnterpriseId(6)
.build());
assertThrows(illegalArgumentExceptionClass, () -> new NetworkCapabilities.Builder()
- .removeEnterpriseCapabilitySubLevel(6)
+ .removeEnterpriseId(6)
.build());
final Class<IllegalStateException> illegalStateException =
IllegalStateException.class;
assertThrows(illegalStateException, () -> new NetworkCapabilities.Builder()
- .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+ .addEnterpriseId(NET_ENTERPRISE_ID_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)
+ .addEnterpriseId(NET_ENTERPRISE_ID_1)
+ .addEnterpriseId(NET_ENTERPRISE_ID_2)
+ .removeEnterpriseId(NET_ENTERPRISE_ID_1)
+ .removeEnterpriseId(NET_ENTERPRISE_ID_2)
.build();
- assertEquals(0, nc4.getEnterpriseCapabilitySubLevels().length);
+ assertEquals(1, nc4.getEnterpriseIds().length);
+ assertTrue(nc4.hasEnterpriseId(NET_ENTERPRISE_ID_1));
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)
+ .addEnterpriseId(NET_ENTERPRISE_ID_1)
+ .addEnterpriseId(NET_ENTERPRISE_ID_2)
+ .removeEnterpriseId(NET_ENTERPRISE_ID_1)
+ .removeEnterpriseId(NET_ENTERPRISE_ID_2)
.build();
assertTrue(nc4.satisfiedByNetworkCapabilities(nc1));
- assertFalse(nc1.satisfiedByNetworkCapabilities(nc4));
+ assertTrue(nc1.satisfiedByNetworkCapabilities(nc4));
assertFalse(nc3.satisfiedByNetworkCapabilities(nc2));
assertTrue(nc2.satisfiedByNetworkCapabilities(nc3));
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 7423c73..c102cb3 100644
--- a/tests/common/java/android/net/NetworkTest.java
+++ b/tests/common/java/android/net/NetworkTest.java
@@ -28,6 +28,7 @@
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;
@@ -46,6 +47,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class NetworkTest {
final Network mNetwork = new Network(99);
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 b69b045..5b28b84 100644
--- a/tests/common/java/android/net/RouteInfoTest.java
+++ b/tests/common/java/android/net/RouteInfoTest.java
@@ -21,7 +21,6 @@
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;
@@ -38,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;
@@ -52,6 +51,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class RouteInfoTest {
@Rule
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
@@ -383,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 a435119..d46fdc9 100644
--- a/tests/common/java/android/net/UidRangeTest.java
+++ b/tests/common/java/android/net/UidRangeTest.java
@@ -35,6 +35,7 @@
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;
@@ -46,6 +47,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class UidRangeTest {
/*
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 f72a458..b684068 100644
--- a/tests/cts/hostside/Android.bp
+++ b/tests/cts/hostside/Android.bp
@@ -25,6 +25,9 @@
"cts-tradefed",
"tradefed",
],
+ static_libs: [
+ "modules-utils-build-testing",
+ ],
// Tag this module as a cts test artifact
test_suites: [
"cts",
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 63572c3..12e7d33 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -18,12 +18,8 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-android_test_helper_app {
- name: "CtsHostsideNetworkTestsApp",
- defaults: [
- "cts_support_defaults",
- "framework-connectivity-test-defaults",
- ],
+java_defaults {
+ name: "CtsHostsideNetworkTestsAppDefaults",
platform_apis: true,
static_libs: [
"CtsHostsideNetworkTestsAidl",
@@ -48,3 +44,28 @@
"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/src/com/android/cts/net/hostside/MyVpnService.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
index 855abf7..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
@@ -18,17 +18,26 @@
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.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 {
@@ -67,39 +76,62 @@
(underlyingNetworks != null) ? underlyingNetworks.toArray(new Network[0]) : null);
}
- private void start(String packageName, Intent intent) {
- Builder builder = new Builder();
+ private String parseIpAndMaskListArgument(String packageName, Intent intent, String argName,
+ BiConsumer<InetAddress, Integer> consumer) {
+ final String addresses = intent.getStringExtra(packageName + "." + argName);
- 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;
- }
- }
+ 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");
@@ -152,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/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 65ea4b1..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
@@ -36,6 +36,7 @@
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;
@@ -71,7 +72,6 @@
import android.net.VpnTransportInfo;
import android.net.cts.util.CtsNetUtils;
import android.net.wifi.WifiManager;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -298,28 +298,54 @@
mActivity.startService(intent);
}
- private void establishVpn(String[] addresses, String[] routes, String allowedApplications,
- String disallowedApplications, @Nullable ProxyInfo proxyInfo,
- @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered)
+ 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 + ".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.
@@ -340,8 +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.
- establishVpn(addresses, routes, allowedApplications, disallowedApplications, proxyInfo,
- underlyingNetworks, isAlwaysMetered);
+ establishVpn(addresses, routes, excludedRoutes, allowedApplications, disallowedApplications,
+ proxyInfo, underlyingNetworks, isAlwaysMetered, addRoutesByIpPrefix);
synchronized (mLock) {
if (mNetwork == null) {
Log.i(TAG, "bf mLock");
@@ -561,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
@@ -584,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 {
@@ -593,7 +625,7 @@
s.receive(p);
fail("Received unexpected reply");
} catch (IOException expected) {
- checkConnectionOwnerUidUdp(s, false);
+ checkConnectionOwnerUidUdp(s, expectConnectionOwnerIsVisible);
}
}
} finally {
@@ -601,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 {
@@ -779,7 +830,7 @@
.getCaps().getUnderlyingNetworks())));
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.S)
+ @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));
@@ -842,7 +893,7 @@
@Test
public void testDefault() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
if (!SdkLevel.isAtLeastS() && (
SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
|| SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) {
@@ -935,7 +986,7 @@
@Test
public void testAppAllowed() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
@@ -956,7 +1007,7 @@
@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);
@@ -974,9 +1025,9 @@
}
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);
@@ -989,8 +1040,76 @@
}
@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");
@@ -1012,7 +1131,7 @@
@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();
@@ -1052,7 +1171,7 @@
@Test
public void testSetProxyDisallowedApps() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
String disallowedApps = mPackageName;
@@ -1078,7 +1197,7 @@
@Test
public void testNoProxy() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
ProxyInfo initialProxy = mCM.getDefaultProxy();
BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
proxyBroadcastReceiver.register();
@@ -1113,7 +1232,7 @@
@Test
public void testBindToNetworkWithProxy() throws Exception {
- if (!supportedHardware()) return;
+ assumeTrue(supportedHardware());
String allowedApps = mPackageName;
Network initialNetwork = mCM.getActiveNetwork();
ProxyInfo initialProxy = mCM.getDefaultProxy();
@@ -1355,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();
@@ -1407,7 +1526,7 @@
*/
@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"},
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 89c79d3..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
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 180015d..3821f87 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -104,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 81c30b1..f66231d 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -49,6 +49,7 @@
"FrameworksNetCommonTests",
"core-tests-support",
"cts-net-utils",
+ "CtsNetTestsNonUpdatableLib",
"ctstestrunner-axt",
"junit",
"junit-params",
diff --git a/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt b/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
index 1a62560..555dd87 100644
--- a/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
+++ b/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
@@ -16,11 +16,11 @@
package android.net.cts
-import android.os.Build
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
@@ -28,7 +28,7 @@
import org.junit.Test
@SmallTest
-@IgnoreUpTo(Build.VERSION_CODES.S)
+@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
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index 4992795..c6fc38f 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -25,6 +25,8 @@
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;
@@ -43,7 +45,6 @@
import android.net.NetworkRequest;
import android.net.ParseException;
import android.net.cts.util.CtsNetUtils;
-import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
@@ -814,7 +815,7 @@
}
/** Verifies that DnsResolver.DnsException can be subclassed and its constructor re-used. */
- @Test @IgnoreUpTo(Build.VERSION_CODES.S)
+ @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) {
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/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/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/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 3735ca4..0bef0e2 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -88,7 +88,9 @@
"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/BpfInterfaceMapUpdaterTest.java",
"java/com/android/server/net/NetworkStats*.java",
+ "java/com/android/server/net/TestableUsageCallback.kt",
]
}
@@ -130,6 +132,7 @@
"platform-compat-test-rules",
"platform-test-annotations",
"service-connectivity-pre-jarjar",
+ "service-connectivity-tiramisu-pre-jarjar",
"services.core-vpn",
],
libs: [
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/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/NetworkIdentityTest.kt b/tests/unit/java/android/net/NetworkIdentityTest.kt
index b1ffc92..ec0420e 100644
--- a/tests/unit/java/android/net/NetworkIdentityTest.kt
+++ b/tests/unit/java/android/net/NetworkIdentityTest.kt
@@ -17,7 +17,11 @@
package android.net
import android.content.Context
+import android.net.ConnectivityManager.MAX_NETWORK_TYPE
+import android.net.ConnectivityManager.TYPE_ETHERNET
import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_NONE
+import android.net.ConnectivityManager.TYPE_WIFI
import android.net.NetworkIdentity.OEM_NONE
import android.net.NetworkIdentity.OEM_PAID
import android.net.NetworkIdentity.OEM_PRIVATE
@@ -30,10 +34,12 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
private const val TEST_IMSI = "testimsi"
+private const val TEST_WIFI_KEY = "testwifikey"
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
@@ -74,12 +80,12 @@
}
@Test
- fun testGetMetered() {
+ fun testIsMetered() {
// Verify network is metered.
val netIdent1 = NetworkIdentity.buildNetworkIdentity(mockContext,
buildMobileNetworkStateSnapshot(NetworkCapabilities(), TEST_IMSI),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
- assertTrue(netIdent1.getMetered())
+ assertTrue(netIdent1.isMetered())
// Verify network is not metered because it has NET_CAPABILITY_NOT_METERED capability.
val capsNotMetered = NetworkCapabilities.Builder().apply {
@@ -88,7 +94,7 @@
val netIdent2 = NetworkIdentity.buildNetworkIdentity(mockContext,
buildMobileNetworkStateSnapshot(capsNotMetered, TEST_IMSI),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
- assertFalse(netIdent2.getMetered())
+ assertFalse(netIdent2.isMetered())
// Verify network is not metered because it has NET_CAPABILITY_TEMPORARILY_NOT_METERED
// capability .
@@ -98,6 +104,120 @@
val netIdent3 = NetworkIdentity.buildNetworkIdentity(mockContext,
buildMobileNetworkStateSnapshot(capsTempNotMetered, TEST_IMSI),
false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
- assertFalse(netIdent3.getMetered())
+ 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)
+
+ // Assert non-wifi can't have wifi network key.
+ assertFailsWith<IllegalArgumentException> {
+ NetworkIdentity.Builder()
+ .setType(TYPE_ETHERNET)
+ .setWifiNetworkKey(TEST_WIFI_KEY)
+ .build()
+ }
+
+ // Assert non-mobile can't have ratType.
+ assertFailsWith<IllegalArgumentException> {
+ NetworkIdentity.Builder()
+ .setType(TYPE_WIFI)
+ .setRatType(TelephonyManager.NETWORK_TYPE_LTE)
+ .build()
+ }
+ }
+
+ @Test
+ fun testBuilder_type() {
+ // Assert illegal type values cannot make an identity.
+ listOf(Integer.MIN_VALUE, TYPE_NONE - 1, MAX_NETWORK_TYPE + 1, Integer.MAX_VALUE)
+ .forEach { type ->
+ assertFailsWith<IllegalArgumentException> {
+ NetworkIdentity.Builder().setType(type).build()
+ }
+ }
+
+ // Verify legitimate type values can make an identity.
+ for (type in TYPE_NONE..MAX_NETWORK_TYPE) {
+ NetworkIdentity.Builder().setType(type).build().also {
+ assertEquals(it.type, type)
+ }
+ }
+ }
+
+ @Test
+ fun testBuilder_ratType() {
+ // Assert illegal ratTypes cannot make an identity.
+ listOf(Integer.MIN_VALUE, NetworkTemplate.NETWORK_TYPE_ALL,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN - 1, Integer.MAX_VALUE)
+ .forEach {
+ assertFailsWith<IllegalArgumentException> {
+ NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setRatType(it)
+ .build()
+ }
+ }
+
+ // Verify legitimate ratTypes can make an identity.
+ TelephonyManager.getAllNetworkTypes().toMutableList().also {
+ it.add(TelephonyManager.NETWORK_TYPE_UNKNOWN)
+ }.forEach { rat ->
+ NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setRatType(rat)
+ .build().also {
+ assertEquals(it.ratType, rat)
+ }
+ }
+ }
+
+ @Test
+ fun testBuilder_oemManaged() {
+ // Assert illegal oemManage values cannot make an identity.
+ listOf(Integer.MIN_VALUE, NetworkTemplate.OEM_MANAGED_ALL, NetworkTemplate.OEM_MANAGED_YES,
+ Integer.MAX_VALUE)
+ .forEach {
+ assertFailsWith<IllegalArgumentException> {
+ NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setRatType(it)
+ .build()
+ }
+ }
+
+ // Verify legitimate oem managed values can make an identity.
+ listOf(NetworkTemplate.OEM_MANAGED_NO, NetworkTemplate.OEM_MANAGED_PAID,
+ NetworkTemplate.OEM_MANAGED_PRIVATE, NetworkTemplate.OEM_MANAGED_PAID or
+ NetworkTemplate.OEM_MANAGED_PRIVATE)
+ .forEach { oemManaged ->
+ NetworkIdentity.Builder()
+ .setOemManaged(oemManaged)
+ .build().also {
+ assertEquals(it.oemManaged, oemManaged)
+ }
+ }
}
}
diff --git a/tests/unit/java/android/net/NetworkStatsAccessTest.java b/tests/unit/java/android/net/NetworkStatsAccessTest.java
index e4fc118..97a93ca 100644
--- a/tests/unit/java/android/net/NetworkStatsAccessTest.java
+++ b/tests/unit/java/android/net/NetworkStatsAccessTest.java
@@ -16,6 +16,8 @@
package android.net;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
@@ -25,7 +27,6 @@
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;
@@ -42,7 +43,7 @@
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
+@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;
diff --git a/tests/unit/java/android/net/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
index 1c557d6..2e82986 100644
--- a/tests/unit/java/android/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -29,6 +29,7 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
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,7 +38,6 @@
import static org.junit.Assert.fail;
import android.content.res.Resources;
-import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.SubscriptionPlan;
@@ -59,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;
@@ -78,7 +79,7 @@
*/
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
+@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";
@@ -195,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());
diff --git a/tests/unit/java/android/net/NetworkStatsHistoryTest.java b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
index c5f8c00..c170605 100644
--- a/tests/unit/java/android/net/NetworkStatsHistoryTest.java
+++ b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
@@ -56,6 +56,7 @@
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
+import java.util.List;
import java.util.Random;
@RunWith(DevSdkIgnoreRunner.class)
@@ -532,6 +533,40 @@
assertEquals(512L + 4096L, stats.getTotalBytes());
}
+ @Test
+ public void testBuilder() {
+ final NetworkStatsHistory.Entry entry1 = new NetworkStatsHistory.Entry(10, 30, 40,
+ 4, 50, 5, 60);
+ final NetworkStatsHistory.Entry entry2 = new NetworkStatsHistory.Entry(30, 15, 3,
+ 41, 7, 1, 0);
+ final NetworkStatsHistory.Entry entry3 = new NetworkStatsHistory.Entry(7, 301, 11,
+ 14, 31, 2, 80);
+
+ final NetworkStatsHistory statsEmpty = new NetworkStatsHistory
+ .Builder(HOUR_IN_MILLIS, 10).build();
+ assertEquals(0, statsEmpty.getEntries().size());
+ assertEquals(HOUR_IN_MILLIS, statsEmpty.getBucketDuration());
+
+ NetworkStatsHistory statsSingle = new NetworkStatsHistory
+ .Builder(HOUR_IN_MILLIS, 8)
+ .addEntry(entry1)
+ .build();
+ assertEquals(1, statsSingle.getEntries().size());
+ assertEquals(HOUR_IN_MILLIS, statsSingle.getBucketDuration());
+ assertEquals(entry1, statsSingle.getEntries().get(0));
+
+ NetworkStatsHistory statsMultiple = new NetworkStatsHistory
+ .Builder(SECOND_IN_MILLIS, 0)
+ .addEntry(entry1).addEntry(entry2).addEntry(entry3)
+ .build();
+ final List<NetworkStatsHistory.Entry> entries = statsMultiple.getEntries();
+ assertEquals(3, entries.size());
+ assertEquals(SECOND_IN_MILLIS, statsMultiple.getBucketDuration());
+ assertEquals(entry1, entries.get(0));
+ assertEquals(entry2, entries.get(1));
+ assertEquals(entry3, entries.get(2));
+ }
+
private static void assertIndexBeforeAfter(
NetworkStatsHistory stats, int before, int after, long time) {
assertEquals("unexpected before", before, stats.getIndexBefore(time));
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 15db45c..048597f 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -22,7 +22,6 @@
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
@@ -57,6 +56,7 @@
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
@@ -95,7 +95,7 @@
oemManaged: Int = OEM_NONE,
metered: Boolean = true
): NetworkStateSnapshot {
- `when`(mockWifiInfo.getCurrentNetworkKey()).thenReturn(wifiKey)
+ `when`(mockWifiInfo.getNetworkKey()).thenReturn(wifiKey)
val lp = LinkProperties()
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !metered)
@@ -312,7 +312,7 @@
val identLteMetered = buildNetworkIdentity(
mockContext, stateMobileImsi1Metered, false, TelephonyManager.NETWORK_TYPE_LTE)
val identCombinedMetered = buildNetworkIdentity(
- mockContext, stateMobileImsi1Metered, false, SUBTYPE_COMBINED)
+ mockContext, stateMobileImsi1Metered, false, NetworkTemplate.NETWORK_TYPE_ALL)
val identImsi2UmtsMetered = buildNetworkIdentity(mockContext,
buildMobileNetworkState(TEST_IMSI2), false, TelephonyManager.NETWORK_TYPE_UMTS)
val identWifi = buildNetworkIdentity(
@@ -326,7 +326,7 @@
val identLteNonMetered = buildNetworkIdentity(
mockContext, stateMobileImsi1NonMetered, false, TelephonyManager.NETWORK_TYPE_LTE)
val identCombinedNonMetered = buildNetworkIdentity(
- mockContext, stateMobileImsi1NonMetered, false, SUBTYPE_COMBINED)
+ mockContext, stateMobileImsi1NonMetered, false, NetworkTemplate.NETWORK_TYPE_ALL)
val identImsi2UmtsNonMetered = buildNetworkIdentity(mockContext,
stateMobileImsi2NonMetered, false, TelephonyManager.NETWORK_TYPE_UMTS)
@@ -556,7 +556,7 @@
}
}
- @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
+ @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
@Test
fun testBuilderMatchRules() {
// Verify unknown match rules cannot construct templates.
@@ -657,7 +657,7 @@
}
}
- @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
+ @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
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index c47604c..652aee9 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -51,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;
@@ -100,6 +101,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_VSIM;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
@@ -250,6 +252,7 @@
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;
@@ -336,6 +339,7 @@
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;
@@ -348,6 +352,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;
@@ -441,6 +446,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;
@@ -490,6 +499,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;
@@ -3386,12 +3397,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
@@ -12245,6 +12256,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");
}
@@ -12255,12 +12268,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);
}
@@ -12278,6 +12297,12 @@
if (null != mTestPackageDefaultNetworkCallback) {
mCm.unregisterNetworkCallback(mTestPackageDefaultNetworkCallback);
}
+ if (null != mProfileDefaultNetworkCallbackAsAppUid2) {
+ mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallbackAsAppUid2);
+ }
+ if (null != mTestPackageDefaultNetworkCallback2) {
+ mCm.unregisterNetworkCallback(mTestPackageDefaultNetworkCallback2);
+ }
}
private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -13706,10 +13731,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 =
@@ -13732,6 +13781,14 @@
return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc);
}
+ private TestNetworkAgentWrapper makeEnterpriseNetworkAgent(int enterpriseId) throws Exception {
+ final NetworkCapabilities workNc = new NetworkCapabilities();
+ workNc.addCapability(NET_CAPABILITY_ENTERPRISE);
+ workNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ workNc.addEnterpriseId(enterpriseId);
+ return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc);
+ }
+
private TestNetworkCallback mEnterpriseCallback;
private UserHandle setupEnterpriseNetwork() {
final UserHandle userHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
@@ -13755,72 +13812,116 @@
}
/**
- * 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(profileNetworkPreference.getPreferenceEnterpriseId());
+ 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 && !connectWorkProfileAgentAhead) {
+ // 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 && !connectWorkProfileAgentAhead) {
+ 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 && !connectWorkProfileAgentAhead) {
+ 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));
+ && nc.hasCapability(NET_CAPABILITY_ENTERPRISE)
+ && nc.hasEnterpriseId(
+ profileNetworkPreference.getPreferenceEnterpriseId())
+ && nc.getEnterpriseIds().length == 1);
+ 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
@@ -13830,7 +13931,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
@@ -13838,32 +13943,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.
@@ -13873,38 +14001,321 @@
// If the control comes here, callbacks seem to behave correctly in the presence of
// a default network when the enterprise network goes up and down. Now, make sure they
// also behave correctly in the absence of a system-wide default network.
- final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent();
+ final TestNetworkAgentWrapper workAgent2 =
+ makeEnterpriseNetworkAgent(profileNetworkPreference.getPreferenceEnterpriseId());
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));
+ && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ && nc.hasEnterpriseId(
+ profileNetworkPreference.getPreferenceEnterpriseId())
+ && nc.getEnterpriseIds().length == 1);
+ 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();
+ registerDefaultNetworkCallbacks();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ 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);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ 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);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ 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.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ 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.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ 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.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ 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.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ 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.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ 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));
+ }
+
+ /**
+ * 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 fallback to default network
+ * along with already connected enterprise work agent
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDownWithFallbackWithAlreadyConnectedWorkAgent()
+ throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), true,
+ 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 for a given enterprise identifier
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDownWithDefaultEnterpriseId()
+ throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), true,
+ 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 for a given enterprise identifier
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDownWithId2()
+ throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(
+ NetworkCapabilities.NET_ENTERPRISE_ID_2);
+ registerDefaultNetworkCallbacks();
+ testPreferenceForUserNetworkUpDownForGivenPreference(
+ profileNetworkPreferenceBuilder.build(), true,
+ 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 for a given enterprise identifier
+ */
+ @Test
+ public void testPreferenceForUserNetworkUpDownWithInvalidId()
+ throws Exception {
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(0);
+ registerDefaultNetworkCallbacks();
+ assertThrows("Should not be able to set invalid enterprise id",
+ IllegalStateException.class, () -> profileNetworkPreferenceBuilder.build());
+ }
+
+ /**
* Test that, in a given networking context, calling setPreferenceForUser to set per-profile
* defaults on then off works as expected.
*/
@@ -14054,10 +14465,16 @@
public void testProfileNetworkPrefWrongPreference() throws Exception {
final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
mServiceContext.setWorkProfile(testHandle, true);
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK + 1);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
assertThrows("Should not be able to set an illegal preference",
IllegalArgumentException.class,
- () -> mCm.setProfileNetworkPreference(testHandle,
- PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null));
+ () -> mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder.build()),
+ null, null));
}
/**
diff --git a/tests/unit/java/com/android/server/NetworkManagementServiceTest.java b/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
index ea29da0..0c58582 100644
--- a/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -32,6 +33,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.INetdUnsolicitedEventListener;
import android.net.LinkAddress;
@@ -71,6 +73,7 @@
public class NetworkManagementServiceTest {
private NetworkManagementService mNMService;
@Mock private Context mContext;
+ @Mock private ConnectivityManager mCm;
@Mock private IBatteryStats.Stub mBatteryStatsService;
@Mock private INetd.Stub mNetdService;
@@ -113,6 +116,9 @@
MockitoAnnotations.initMocks(this);
doNothing().when(mNetdService)
.registerUnsolicitedEventListener(mUnsolListenerCaptor.capture());
+ doReturn(Context.CONNECTIVITY_SERVICE).when(mContext).getSystemServiceName(
+ eq(ConnectivityManager.class));
+ doReturn(mCm).when(mContext).getSystemService(eq(Context.CONNECTIVITY_SERVICE));
// Start the service and wait until it connects to our socket.
mNMService = NetworkManagementService.create(mContext, mDeps);
}
@@ -239,6 +245,7 @@
mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, true);
assertTrue("Should be true since mobile data usage is restricted",
mNMService.isNetworkRestricted(TEST_UID));
+ verify(mCm).updateMeteredNetworkDenyList(TEST_UID, true /* enabled */);
mNMService.setDataSaverModeEnabled(true);
verify(mNetdService).bandwidthEnableDataSaver(true);
@@ -246,13 +253,16 @@
mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, false);
assertTrue("Should be true since data saver is on and the uid is not allowlisted",
mNMService.isNetworkRestricted(TEST_UID));
+ verify(mCm).updateMeteredNetworkDenyList(TEST_UID, true /* false */);
mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, true);
assertFalse("Should be false since data saver is on and the uid is allowlisted",
mNMService.isNetworkRestricted(TEST_UID));
+ verify(mCm).updateMeteredNetworkAllowList(TEST_UID, true /* enabled */);
// remove uid from allowlist and turn datasaver off again
mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false);
+ verify(mCm).updateMeteredNetworkAllowList(TEST_UID, false /* enabled */);
mNMService.setDataSaverModeEnabled(false);
verify(mNetdService).bandwidthEnableDataSaver(false);
assertFalse("Network should not be restricted when data saver is off",
@@ -306,12 +316,14 @@
for (int chain : chains) {
final ArrayMap<Integer, Boolean> expectedValues = expected.get(chain);
mNMService.setFirewallChainEnabled(chain, true);
+ verify(mCm).setFirewallChainEnabled(chain, true /* enabled */);
for (int state : states) {
mNMService.setFirewallUidRule(chain, TEST_UID, state);
assertEquals(errorMsg.apply(chain, state),
expectedValues.get(state), mNMService.isNetworkRestricted(TEST_UID));
}
mNMService.setFirewallChainEnabled(chain, false);
+ verify(mCm).setFirewallChainEnabled(chain, false /* enabled */);
}
}
}
diff --git a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
index a3da05d..ec51537 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
@@ -106,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;
@@ -164,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);
}
@@ -201,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");
@@ -214,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,
@@ -224,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]);
}
@@ -235,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);
@@ -283,7 +296,7 @@
false /* roaming */);
verify(mStatsManager, times(1)).registerUsageCallback(
- any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+ any(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
}
@Test
@@ -291,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 */,
@@ -300,7 +315,7 @@
// Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB
verify(mStatsManager, times(1)).registerUsageCallback(
- any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+ any(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
}
@Test
@@ -308,16 +323,18 @@
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 */);
// Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB
verify(mStatsManager, times(1)).registerUsageCallback(
- any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+ any(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
}
@Test
@@ -334,7 +351,7 @@
// Default global setting should be used: 12 - 7 = 5
verify(mStatsManager, times(1)).registerUsageCallback(
- any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(5)), any(), any());
+ any(), eq(DataUnit.MEGABYTES.toBytes(5)), any(), any());
}
@Test
@@ -349,7 +366,7 @@
false /* roaming */);
verify(mStatsManager, times(1)).registerUsageCallback(
- any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+ any(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
// Update setting
setDefaultQuotaGlobalSetting(DataUnit.MEGABYTES.toBytes(14));
@@ -359,7 +376,7 @@
// Callback must have been re-registered with new setting
verify(mStatsManager, times(1)).unregisterUsageCallback(any());
verify(mStatsManager, times(1)).registerUsageCallback(
- any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+ any(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
}
@Test
@@ -374,7 +391,7 @@
false /* roaming */);
verify(mStatsManager, times(1)).registerUsageCallback(
- any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+ any(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes))
.thenReturn((int) DataUnit.MEGABYTES.toBytes(16));
@@ -385,6 +402,6 @@
// Uses the new setting (16 - 2 = 14MB)
verify(mStatsManager, times(1)).registerUsageCallback(
- any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(14)), any(), any());
+ any(), eq(DataUnit.MEGABYTES.toBytes(14)), any(), any());
}
}
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/net/BpfInterfaceMapUpdaterTest.java b/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java
new file mode 100644
index 0000000..987b7b7
--- /dev/null
+++ b/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.net;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.INetd;
+import android.net.MacAddress;
+import android.os.Handler;
+import android.os.test.TestLooper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.IBpfMap;
+import com.android.net.module.util.InterfaceParams;
+import com.android.net.module.util.Struct.U32;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class BpfInterfaceMapUpdaterTest {
+ private static final int TEST_INDEX = 1;
+ private static final int TEST_INDEX2 = 2;
+ private static final String TEST_INTERFACE_NAME = "test1";
+ private static final String TEST_INTERFACE_NAME2 = "test2";
+
+ private final TestLooper mLooper = new TestLooper();
+ private BaseNetdUnsolicitedEventListener mListener;
+ private BpfInterfaceMapUpdater mUpdater;
+ @Mock private IBpfMap<U32, InterfaceMapValue> mBpfMap;
+ @Mock private INetd mNetd;
+ @Mock private Context mContext;
+
+ private class TestDependencies extends BpfInterfaceMapUpdater.Dependencies {
+ @Override
+ public IBpfMap<U32, InterfaceMapValue> getInterfaceMap() {
+ return mBpfMap;
+ }
+
+ @Override
+ public InterfaceParams getInterfaceParams(String ifaceName) {
+ if (ifaceName.equals(TEST_INTERFACE_NAME)) {
+ return new InterfaceParams(TEST_INTERFACE_NAME, TEST_INDEX,
+ MacAddress.ALL_ZEROS_ADDRESS);
+ } else if (ifaceName.equals(TEST_INTERFACE_NAME2)) {
+ return new InterfaceParams(TEST_INTERFACE_NAME2, TEST_INDEX2,
+ MacAddress.ALL_ZEROS_ADDRESS);
+ }
+
+ return null;
+ }
+
+ @Override
+ public INetd getINetd(Context ctx) {
+ return mNetd;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mNetd.interfaceGetList()).thenReturn(new String[] {TEST_INTERFACE_NAME});
+ mUpdater = new BpfInterfaceMapUpdater(mContext, new Handler(mLooper.getLooper()),
+ new TestDependencies());
+ }
+
+ private void verifyStartUpdater() throws Exception {
+ mUpdater.start();
+ mLooper.dispatchAll();
+ final ArgumentCaptor<BaseNetdUnsolicitedEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(BaseNetdUnsolicitedEventListener.class);
+ verify(mNetd).registerUnsolicitedEventListener(listenerCaptor.capture());
+ mListener = listenerCaptor.getValue();
+ verify(mBpfMap).updateEntry(eq(new U32(TEST_INDEX)),
+ eq(new InterfaceMapValue(TEST_INTERFACE_NAME)));
+ }
+
+ @Test
+ public void testUpdateInterfaceMap() throws Exception {
+ verifyStartUpdater();
+
+ mListener.onInterfaceAdded(TEST_INTERFACE_NAME2);
+ mLooper.dispatchAll();
+ verify(mBpfMap).updateEntry(eq(new U32(TEST_INDEX2)),
+ eq(new InterfaceMapValue(TEST_INTERFACE_NAME2)));
+
+ // Check that when onInterfaceRemoved is called, nothing happens.
+ mListener.onInterfaceRemoved(TEST_INTERFACE_NAME);
+ mLooper.dispatchAll();
+ verifyNoMoreInteractions(mBpfMap);
+ }
+}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index 8340a13..0e2c293 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -32,9 +32,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import android.content.Context;
import android.content.res.Resources;
-import android.net.INetd;
+import android.net.ConnectivityManager;
import android.net.NetworkStats;
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
@@ -73,8 +76,8 @@
private File mTestProc;
private NetworkStatsFactory mFactory;
- @Mock
- private INetd mNetd;
+ @Mock private Context mContext;
+ @Mock private ConnectivityManager mCm;
@Before
public void setUp() throws Exception {
@@ -85,7 +88,10 @@
// 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, mNetd);
+ doReturn(Context.CONNECTIVITY_SERVICE).when(mContext).getSystemServiceName(
+ eq(ConnectivityManager.class));
+ doReturn(mCm).when(mContext).getSystemService(eq(Context.CONNECTIVITY_SERVICE));
+ mFactory = new NetworkStatsFactory(mContext, mTestProc, false);
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 416549c..6a7da9e 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -29,25 +29,23 @@
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;
import static org.mockito.Matchers.anyInt;
-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.net.netstats.IUsageCallback;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Messenger;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
@@ -55,7 +53,6 @@
import androidx.test.filters.SmallTest;
-import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -74,7 +71,7 @@
*/
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
+@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";
@@ -96,21 +93,15 @@
private static final long WAIT_TIMEOUT_MS = 500;
private static final long THRESHOLD_BYTES = 2 * MB_IN_BYTES;
private static final long BASE_BYTES = 7 * MB_IN_BYTES;
- private static final int INVALID_TYPE = -1;
-
- private long mElapsedRealtime;
private HandlerThread mObserverHandlerThread;
- private Handler mObserverNoopHandler;
-
- private LatchedHandler mHandler;
private NetworkStatsObservers mStatsObservers;
- private Messenger mMessenger;
private ArrayMap<String, NetworkIdentitySet> mActiveIfaces;
private ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces;
- @Mock private IBinder mockBinder;
+ @Mock private IBinder mUsageCallbackBinder;
+ @Mock private IUsageCallback mUsageCallback;
@Before
public void setUp() throws Exception {
@@ -126,11 +117,9 @@
}
};
- mHandler = new LatchedHandler(Looper.getMainLooper(), new ConditionVariable());
- mMessenger = new Messenger(mHandler);
-
mActiveIfaces = new ArrayMap<>();
mActiveUidIfaces = new ArrayMap<>();
+ Mockito.when(mUsageCallback.asBinder()).thenReturn(mUsageCallbackBinder);
}
@Test
@@ -139,7 +128,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdTooLowBytes);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateWifi, request.template));
@@ -152,7 +141,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, highThresholdBytes);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateWifi, request.template));
@@ -164,13 +153,13 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, THRESHOLD_BYTES);
- DataUsageRequest request1 = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request1 = mStatsObservers.register(inputRequest, mUsageCallback,
Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
assertTrue(request1.requestId > 0);
assertTrue(Objects.equals(sTemplateWifi, request1.template));
assertEquals(THRESHOLD_BYTES, request1.thresholdInBytes);
- DataUsageRequest request2 = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request2 = mStatsObservers.register(inputRequest, mUsageCallback,
Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
assertTrue(request2.requestId > request1.requestId);
assertTrue(Objects.equals(sTemplateWifi, request2.template));
@@ -190,17 +179,19 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+ Mockito.verify(mUsageCallbackBinder).linkToDeath(any(IBinder.DeathRecipient.class),
+ anyInt());
mStatsObservers.unregister(request, Process.SYSTEM_UID);
waitForObserverToIdle();
- Mockito.verify(mockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+ Mockito.verify(mUsageCallbackBinder).unlinkToDeath(any(IBinder.DeathRecipient.class),
+ anyInt());
}
@Test
@@ -208,17 +199,18 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
UID_RED, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+ Mockito.verify(mUsageCallbackBinder)
+ .linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
mStatsObservers.unregister(request, UID_BLUE);
waitForObserverToIdle();
- Mockito.verifyZeroInteractions(mockBinder);
+ Mockito.verifyZeroInteractions(mUsageCallbackBinder);
}
private NetworkIdentitySet makeTestIdentSet() {
@@ -235,7 +227,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
@@ -259,7 +251,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
@@ -289,7 +281,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
@@ -312,7 +304,7 @@
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
+ Mockito.verify(mUsageCallback).onThresholdReached(any());
}
@Test
@@ -320,7 +312,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
UID_RED, NetworkStatsAccess.Level.DEFAULT);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
@@ -345,7 +337,7 @@
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
+ Mockito.verify(mUsageCallback).onThresholdReached(any());
}
@Test
@@ -353,7 +345,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
UID_BLUE, NetworkStatsAccess.Level.DEFAULT);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
@@ -385,7 +377,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
UID_BLUE, NetworkStatsAccess.Level.USER);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
@@ -410,7 +402,7 @@
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
+ Mockito.verify(mUsageCallback).onThresholdReached(any());
}
@Test
@@ -418,7 +410,7 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
- DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mUsageCallback,
UID_RED, NetworkStatsAccess.Level.USER);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
@@ -447,6 +439,5 @@
private void waitForObserverToIdle() {
HandlerUtils.waitForIdle(mObserverHandlerThread, WAIT_TIMEOUT_MS);
- HandlerUtils.waitForIdle(mHandler, WAIT_TIMEOUT_MS);
}
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 513f485..94a4f3d 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;
@@ -67,26 +66,25 @@
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;
import android.annotation.NonNull;
import android.app.AlarmManager;
-import android.app.usage.NetworkStatsManager;
import android.content.Context;
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,19 +94,15 @@
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;
import android.os.PowerManager;
import android.os.SimpleClock;
import android.provider.Settings;
@@ -118,8 +112,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;
@@ -184,19 +179,23 @@
private MockContext mServiceContext;
private @Mock TelephonyManager mTelephonyManager;
private static @Mock WifiInfo sWifiInfo;
- private @Mock INetworkManagementService mNetManager;
+ private @Mock INetd mNetd;
private @Mock TetheringManager mTetheringManager;
private @Mock NetworkStatsFactory mStatsFactory;
private @Mock NetworkStatsSettings mSettings;
- private @Mock IBinder mBinder;
+ private @Mock IBinder mUsageCallbackBinder;
+ private TestableUsageCallback mUsageCallback;
private @Mock AlarmManager mAlarmManager;
@Mock
private NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
+ private @Mock BpfInterfaceMapUpdater mBpfInterfaceMapUpdater;
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;
@@ -243,12 +242,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(sWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
+ when(mLocationPermissionChecker.checkCallersLocationPermission(
+ any(), any(), anyInt(), anyBoolean(), any())).thenReturn(true);
+ when(sWifiInfo.getNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
mStatsDir = TestIoUtils.createTemporaryDirectory(getClass().getSimpleName());
PowerManager powerManager = (PowerManager) mServiceContext.getSystemService(
@@ -258,7 +273,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);
@@ -283,11 +298,11 @@
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 =
@@ -295,6 +310,8 @@
verify(mTetheringManager).registerTetheringEventCallback(
any(), tetheringEventCbCaptor.capture());
mTetheringEventCallback = tetheringEventCbCaptor.getValue();
+
+ mUsageCallback = new TestableUsageCallback(mUsageCallbackBinder);
}
@NonNull
@@ -307,7 +324,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;
@@ -320,6 +337,16 @@
return mContentObserver = super.makeContentObserver(handler, settings, monitor);
}
+ @Override
+ public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
+ return mLocationPermissionChecker;
+ }
+
+ @Override
+ public BpfInterfaceMapUpdater makeBpfInterfaceMapUpdater(
+ @NonNull Context ctx, @NonNull Handler handler) {
+ return mBpfInterfaceMapUpdater;
+ }
};
}
@@ -328,7 +355,7 @@
mServiceContext = null;
mStatsDir = null;
- mNetManager = null;
+ mNetd = null;
mSettings = null;
mSession.close();
@@ -971,7 +998,7 @@
}
@Test
- public void testDetailedUidStats() throws Exception {
+ public void testUidStatsForTransport() throws Exception {
// pretend that network comes online
expectDefaultSettings();
NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
@@ -997,7 +1024,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;
@@ -1008,68 +1035,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();
@@ -1249,12 +1214,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
@@ -1281,20 +1245,14 @@
DataUsageRequest inputRequest = new DataUsageRequest(
DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdInBytes);
- // Create a messenger that waits for callback activity
- ConditionVariable cv = new ConditionVariable(false);
- LatchedHandler latchedHandler = new LatchedHandler(Looper.getMainLooper(), cv);
- Messenger messenger = new Messenger(latchedHandler);
-
// Force poll
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
// Register and verify request and that binder was called
- DataUsageRequest request =
- mService.registerUsageCallback(mServiceContext.getOpPackageName(), inputRequest,
- messenger, mBinder);
+ DataUsageRequest request = mService.registerUsageCallback(
+ mServiceContext.getOpPackageName(), inputRequest, mUsageCallback);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateWifi, request.template));
long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB
@@ -1303,7 +1261,7 @@
HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
// Make sure that the caller binder gets connected
- verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+ verify(mUsageCallbackBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
// modify some number on wifi, and trigger poll event
// not enough traffic to call data usage callback
@@ -1318,7 +1276,7 @@
assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
// make sure callback has not being called
- assertEquals(INVALID_TYPE, latchedHandler.lastMessageType);
+ mUsageCallback.assertNoCallback();
// and bump forward again, with counters going higher. this is
// important, since it will trigger the data usage callback
@@ -1333,23 +1291,21 @@
assertNetworkTotal(sTemplateWifi, 4096000L, 4L, 8192000L, 8L, 0);
- // Wait for the caller to ack receipt of CALLBACK_LIMIT_REACHED
- assertTrue(cv.block(WAIT_TIMEOUT));
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.lastMessageType);
- cv.close();
+ // Wait for the caller to invoke expectOnThresholdReached.
+ mUsageCallback.expectOnThresholdReached();
// Allow binder to disconnect
- when(mBinder.unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt())).thenReturn(true);
+ when(mUsageCallbackBinder.unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt()))
+ .thenReturn(true);
// Unregister request
mService.unregisterUsageRequest(request);
- // Wait for the caller to ack receipt of CALLBACK_RELEASED
- assertTrue(cv.block(WAIT_TIMEOUT));
- assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.lastMessageType);
+ // Wait for the caller to invoke expectOnCallbackReleased.
+ mUsageCallback.expectOnCallbackReleased();
// Make sure that the caller binder gets disconnected
- verify(mBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+ verify(mUsageCallbackBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
}
@Test
@@ -1675,6 +1631,28 @@
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();
@@ -1690,7 +1668,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
@@ -1748,16 +1727,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 {
@@ -1901,21 +1881,4 @@
private void waitForIdle() {
HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
}
-
- static class LatchedHandler extends Handler {
- private final ConditionVariable mCv;
- int lastMessageType = INVALID_TYPE;
-
- LatchedHandler(Looper looper, ConditionVariable cv) {
- super(looper);
- mCv = cv;
- }
-
- @Override
- public void handleMessage(Message msg) {
- lastMessageType = msg.what;
- mCv.open();
- super.handleMessage(msg);
- }
- }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 2add3ee..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,11 +38,10 @@
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;
@@ -79,19 +81,23 @@
private final Executor mExecutor = Executors.newSingleThreadExecutor();
private NetworkStatsSubscriptionsMonitor mMonitor;
- private TestLooper mTestLooper = new TestLooper();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ // 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
@@ -117,14 +123,29 @@
return list;
}
+ 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) {
- final ServiceState serviceState = mock(ServiceState.class);
- when(serviceState.getDataNetworkType()).thenReturn(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) {
@@ -151,11 +172,11 @@
mMonitor.onSubscriptionsChanged();
}
- private void assertAndCaptureRatTypeListenerRegistrationWith(int subId, int listenedState) {
+ private void assertAndCaptureRatTypeListenerRegistration(int subId) {
final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
ArgumentCaptor.forClass(RatTypeListener.class);
- verify(mTelephonyManagerOfSub.get(subId)).listen(ratTypeListenerCaptor.capture(),
- eq(listenedState));
+ verify(mTelephonyManagerOfSub.get(subId))
+ .registerTelephonyCallback(any(), ratTypeListenerCaptor.capture());
final RatTypeListener listener = CollectionUtils
.find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == subId);
assertNotNull(listener);
@@ -168,8 +189,14 @@
final int[] subList = convertArrayListToIntArray(mTestSubList);
when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList);
mMonitor.onSubscriptionsChanged();
+ assertRatTypeListenerDeregistration(subId);
mRatTypeListenerOfSub.delete(subId);
- // Keep TelephonyManagerOfSubs so the test could verify de-registration.
+ mTelephonyManagerOfSub.delete(subId);
+ }
+
+ private void assertRatTypeListenerDeregistration(int subId) {
+ verify(mTelephonyManagerOfSub.get(subId))
+ .unregisterTelephonyCallback(eq(mRatTypeListenerOfSub.get(subId)));
}
private void assertRatTypeChangedForSub(String subscriberId, int ratType) {
@@ -199,10 +226,8 @@
// Insert sim2.
addTestSub(TEST_SUBID2, TEST_IMSI2);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_SERVICE_STATE);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID2,
- PhoneStateListener.LISTEN_SERVICE_STATE);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID2);
reset(mDelegate);
// Set RAT type of sim1 to UMTS.
@@ -226,8 +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);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID2,
- PhoneStateListener.LISTEN_NONE);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
@@ -240,10 +263,7 @@
reset(mDelegate);
mMonitor.stop();
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_NONE);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID2,
- PhoneStateListener.LISTEN_NONE);
+ assertRatTypeListenerDeregistration(TEST_SUBID1);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
}
@@ -255,29 +275,26 @@
// before changing RAT type. Also capture listener for later use.
addTestSub(TEST_SUBID1, TEST_IMSI1);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_SERVICE_STATE);
+ 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);
@@ -287,9 +304,7 @@
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);
}
@@ -305,8 +320,7 @@
// Set IMSI for sim1, verify the listener will be registered.
updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_SERVICE_STATE);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
reset(mTelephonyManager);
// Set RAT type of sim1 to UMTS. Verify RAT type of sim1 is changed.
@@ -317,8 +331,7 @@
// 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);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_NONE);
+ assertRatTypeListenerDeregistration(TEST_SUBID1);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
reset(mDelegate);
clearInvocations(mTelephonyManagerOfSub.get(TEST_SUBID1));
@@ -328,8 +341,7 @@
final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
ArgumentCaptor.forClass(RatTypeListener.class);
updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_SERVICE_STATE);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
reset(mDelegate);
clearInvocations(mTelephonyManagerOfSub.get(TEST_SUBID1));
@@ -340,8 +352,7 @@
reset(mDelegate);
mMonitor.stop();
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_NONE);
+ assertRatTypeListenerDeregistration(TEST_SUBID1);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
}
@@ -357,8 +368,7 @@
// Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
// before changing RAT type.
addTestSub(TEST_SUBID1, TEST_IMSI1);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_SERVICE_STATE);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
// Set RAT type of sim1 to UMTS.
@@ -373,10 +383,9 @@
// NETWORK_TYPE_UNKNOWN until received initial callback from telephony.
updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2);
final RatTypeListener oldListener = mRatTypeListenerOfSub.get(TEST_SUBID1);
- assertAndCaptureRatTypeListenerRegistrationWith(TEST_SUBID1,
- PhoneStateListener.LISTEN_SERVICE_STATE);
+ assertAndCaptureRatTypeListenerRegistration(TEST_SUBID1);
verify(mTelephonyManagerOfSub.get(TEST_SUBID1), times(1))
- .listen(eq(oldListener), eq(PhoneStateListener.LISTEN_NONE));
+ .unregisterTelephonyCallback(eq(oldListener));
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
reset(mDelegate);
diff --git a/tests/unit/java/com/android/server/net/TestableUsageCallback.kt b/tests/unit/java/com/android/server/net/TestableUsageCallback.kt
new file mode 100644
index 0000000..44f588c
--- /dev/null
+++ b/tests/unit/java/com/android/server/net/TestableUsageCallback.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.net
+
+import android.net.DataUsageRequest
+import android.net.netstats.IUsageCallback
+import android.os.IBinder
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+import kotlin.test.assertEquals
+import kotlin.test.fail
+
+private const val DEFAULT_TIMEOUT_MS = 200L
+
+// TODO: Move the class to static libs once all downstream have IUsageCallback definition.
+open class TestableUsageCallback(private val binder: IBinder) : IUsageCallback.Stub() {
+ sealed class CallbackType {
+ object OnThresholdReached : CallbackType()
+ object OnCallbackReleased : CallbackType()
+ }
+
+ // TODO: Change to use ArrayTrackRecord once moved into to the module.
+ private val history = LinkedBlockingQueue<CallbackType>()
+
+ override fun onThresholdReached(request: DataUsageRequest) {
+ history.add(CallbackType.OnThresholdReached)
+ }
+
+ override fun onCallbackReleased(request: DataUsageRequest) {
+ history.add(CallbackType.OnCallbackReleased)
+ }
+
+ fun expectOnThresholdReached() {
+ assertEquals(CallbackType.OnThresholdReached,
+ history.poll(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ }
+
+ fun expectOnCallbackReleased() {
+ assertEquals(CallbackType.OnCallbackReleased,
+ history.poll(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ }
+
+ @JvmOverloads
+ fun assertNoCallback(timeout: Long = DEFAULT_TIMEOUT_MS) {
+ val cb = history.poll(timeout, TimeUnit.MILLISECONDS)
+ cb?.let { fail("Expected no callback but got $cb") }
+ }
+
+ override fun asBinder(): IBinder {
+ return binder
+ }
+}
\ No newline at end of file