Merge "Test UidCounterSetMap in NetworkStatsServiceTest"
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 041a3ae..c0e6749 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -53,6 +53,7 @@
         "framework-statsd.stubs.module_lib",
         "framework-tethering.impl",
         "framework-wifi",
+        "framework-bluetooth",
         "unsupportedappusage",
     ],
     plugins: ["java_api_finder"],
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index db08c48..8da421d 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -127,6 +127,7 @@
 
   public static final class NetworkAgentConfig.Builder {
     method @NonNull public android.net.NetworkAgentConfig.Builder setBypassableVpn(boolean);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setExcludeLocalRoutesVpn(boolean);
     method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
   }
 
@@ -146,7 +147,9 @@
   }
 
   public class NetworkRequest implements android.os.Parcelable {
+    method @NonNull public int[] getEnterpriseIds();
     method @NonNull public int[] getForbiddenCapabilities();
+    method public boolean hasEnterpriseId(int);
     method public boolean hasForbiddenCapability(int);
   }
 
diff --git a/framework/src/android/net/NetworkAgentConfig.java b/framework/src/android/net/NetworkAgentConfig.java
index 93fc379..040bf31 100644
--- a/framework/src/android/net/NetworkAgentConfig.java
+++ b/framework/src/android/net/NetworkAgentConfig.java
@@ -425,8 +425,10 @@
          * Sets whether the local traffic is exempted from VPN.
          *
          * @return this builder, to facilitate chaining.
-         * @hide TODO(184750836): Unhide once the implementation is completed.
+         * @hide
          */
+        @NonNull
+        @SystemApi(client = MODULE_LIBRARIES)
         public Builder setExcludeLocalRoutesVpn(boolean excludeLocalRoutes) {
             mConfig.excludeLocalRouteVpn = excludeLocalRoutes;
             return this;
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
index afc76d6..b7a6076 100644
--- a/framework/src/android/net/NetworkRequest.java
+++ b/framework/src/android/net/NetworkRequest.java
@@ -725,6 +725,33 @@
     }
 
     /**
+     * Get the enteprise identifiers.
+     *
+     * Get all the enterprise identifiers set on this {@code NetworkCapability}
+     * @return array of all the enterprise identifiers.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public @NonNull @NetworkCapabilities.EnterpriseId int[] getEnterpriseIds() {
+        // No need to make a defensive copy here as NC#getCapabilities() already returns
+        // a new array.
+        return networkCapabilities.getEnterpriseIds();
+    }
+
+    /**
+     * Tests for the presence of an enterprise identifier on this instance.
+     *
+     * @param enterpriseId the enterprise capability identifier to be tested for.
+     * @return {@code true} if set on this instance.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public boolean hasEnterpriseId(
+            @NetworkCapabilities.EnterpriseId int enterpriseId) {
+        return networkCapabilities.hasEnterpriseId(enterpriseId);
+    }
+
+    /**
      * Gets all the forbidden capabilities set on this {@code NetworkRequest} instance.
      *
      * @return an array of forbidden capability values for this instance.
diff --git a/service/Android.bp b/service/Android.bp
index d1a9004..3afd635 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -110,6 +110,7 @@
         "networkstack-client",
         "PlatformProperties",
         "service-connectivity-protos",
+        "NetworkStackApiStableShims",
     ],
     apex_available: [
         "com.android.tethering",
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index cc0cbcc..f658a5e 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -94,5 +94,8 @@
 rule com.android.internal.util.IState* com.android.connectivity.@0
 rule com.android.internal.util.State* com.android.connectivity.@0
 
+# From the API shims
+rule com.android.networkstack.apishim.** 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
index eeaad18..b445462 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -426,6 +426,43 @@
     return pid;
 }
 
+// Stop clatd process. SIGTERM with timeout first, if fail, SIGKILL.
+// See stopProcess() in system/netd/server/NetdConstants.cpp.
+// TODO: have a function stopProcess(int pid, const char *name) in common location and call it.
+static constexpr int WAITPID_ATTEMPTS = 50;
+static constexpr int WAITPID_RETRY_INTERVAL_US = 100000;
+
+static void stopClatdProcess(int pid) {
+    int err = kill(pid, SIGTERM);
+    if (err) {
+        err = errno;
+    }
+    if (err == ESRCH) {
+        ALOGE("clatd child process %d unexpectedly disappeared", pid);
+        return;
+    }
+    if (err) {
+        ALOGE("Error killing clatd child process %d: %s", pid, strerror(err));
+    }
+    int status = 0;
+    int ret = 0;
+    for (int count = 0; ret == 0 && count < WAITPID_ATTEMPTS; count++) {
+        usleep(WAITPID_RETRY_INTERVAL_US);
+        ret = waitpid(pid, &status, WNOHANG);
+    }
+    if (ret == 0) {
+        ALOGE("Failed to SIGTERM clatd pid=%d, try SIGKILL", pid);
+        // TODO: fix that kill failed or waitpid doesn't return.
+        kill(pid, SIGKILL);
+        ret = waitpid(pid, &status, 0);
+    }
+    if (ret == -1) {
+        ALOGE("Error waiting for clatd child process %d: %s", pid, strerror(errno));
+    } else {
+        ALOGD("clatd process %d terminated status=%d", pid, status);
+    }
+}
+
 static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jobject clazz,
                                                                       jstring iface, jstring pfx96,
                                                                       jstring v4, jstring v6,
@@ -448,8 +485,7 @@
         }
     }
 
-    kill(pid, SIGTERM);
-    waitpid(pid, nullptr, 0);  // Should we block in JNI?
+    stopClatdProcess(pid);
 }
 
 /*
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 7616457..c57983b 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -220,7 +220,7 @@
             @NonNull final IpPrefix nat64Prefix)
             throws IOException {
         if (mIface != null || mPid != INVALID_PID) {
-            throw new IOException("Clatd has started on " + mIface + " (pid " + mPid + ")");
+            throw new IOException("Clatd is already running on " + mIface + " (pid " + mPid + ")");
         }
         if (nat64Prefix.getPrefixLength() != 96) {
             throw new IOException("Prefix must be 96 bits long: " + nat64Prefix);
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 2b698fd..17133c7 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -74,6 +74,7 @@
         "java/android/net/TelephonyNetworkSpecifierTest.java",
         "java/android/net/VpnManagerTest.java",
         "java/android/net/ipmemorystore/*.java",
+        "java/android/net/netstats/NetworkStatsDataMigrationUtilsTest.kt",
         "java/android/net/nsd/*.java",
         "java/com/android/internal/net/NetworkUtilsInternalTest.java",
         "java/com/android/internal/net/VpnProfileTest.java",
@@ -143,6 +144,7 @@
         "ServiceConnectivityResources",
     ],
     visibility: ["//packages/modules/Connectivity/tests:__subpackages__"],
+    exclude_kotlinc_generated_files: false,
 }
 
 android_test {
diff --git a/tests/unit/java/android/net/NetworkIdentityTest.kt b/tests/unit/java/android/net/NetworkIdentityTest.kt
index 1b512f0..4b2d874 100644
--- a/tests/unit/java/android/net/NetworkIdentityTest.kt
+++ b/tests/unit/java/android/net/NetworkIdentityTest.kt
@@ -26,6 +26,7 @@
 import android.net.NetworkIdentity.OEM_PAID
 import android.net.NetworkIdentity.OEM_PRIVATE
 import android.net.NetworkIdentity.getOemBitfield
+import android.app.usage.NetworkStatsManager
 import android.telephony.TelephonyManager
 import android.os.Build
 import com.android.testutils.DevSdkIgnoreRule
@@ -171,7 +172,7 @@
     fun testBuilder_ratType() {
         // Assert illegal ratTypes cannot make an identity.
         listOf(Integer.MIN_VALUE, NetworkTemplate.NETWORK_TYPE_ALL,
-                NetworkTemplate.NETWORK_TYPE_5G_NSA - 1, Integer.MAX_VALUE)
+                NetworkStatsManager.NETWORK_TYPE_5G_NSA - 1, Integer.MAX_VALUE)
                 .forEach {
                     assertFailsWith<IllegalArgumentException> {
                         NetworkIdentity.Builder()
@@ -184,7 +185,7 @@
         // Verify legitimate ratTypes can make an identity.
         TelephonyManager.getAllNetworkTypes().toMutableList().also {
             it.add(TelephonyManager.NETWORK_TYPE_UNKNOWN)
-            it.add(NetworkTemplate.NETWORK_TYPE_5G_NSA)
+            it.add(NetworkStatsManager.NETWORK_TYPE_5G_NSA)
         }.forEach { rat ->
             NetworkIdentity.Builder()
                     .setType(TYPE_MOBILE)
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index 048597f..453612f 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -16,6 +16,7 @@
 
 package android.net
 
+import android.app.usage.NetworkStatsManager.NETWORK_TYPE_5G_NSA
 import android.content.Context
 import android.net.ConnectivityManager.TYPE_MOBILE
 import android.net.ConnectivityManager.TYPE_WIFI
@@ -36,7 +37,6 @@
 import android.net.NetworkTemplate.MATCH_PROXY
 import android.net.NetworkTemplate.MATCH_WIFI
 import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD
-import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA
 import android.net.NetworkTemplate.NETWORK_TYPE_ALL
 import android.net.NetworkTemplate.OEM_MANAGED_ALL
 import android.net.NetworkTemplate.OEM_MANAGED_NO
diff --git a/tests/unit/java/android/net/netstats/NetworkStatsDataMigrationUtilsTest.kt b/tests/unit/java/android/net/netstats/NetworkStatsDataMigrationUtilsTest.kt
new file mode 100644
index 0000000..e4943ea
--- /dev/null
+++ b/tests/unit/java/android/net/netstats/NetworkStatsDataMigrationUtilsTest.kt
@@ -0,0 +1,112 @@
+/*
+ * 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 android.net.netstats
+
+import android.net.NetworkStatsCollection
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.SmallTest
+import com.android.frameworks.tests.net.R
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.SC_V2
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+import java.io.DataInputStream
+import java.net.ProtocolException
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.fail
+
+private const val BUCKET_DURATION_MS = 2 * 60 * 60 * 1000L
+
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+class NetworkStatsDataMigrationUtilsTest {
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun testReadPlatformCollection() {
+        // Verify the method throws for wrong file version.
+        assertFailsWith<ProtocolException> {
+            NetworkStatsDataMigrationUtils.readPlatformCollection(
+                    NetworkStatsCollection.Builder(BUCKET_DURATION_MS),
+                    getInputStreamForResource(R.raw.netstats_uid_v4))
+        }
+
+        val builder = NetworkStatsCollection.Builder(BUCKET_DURATION_MS)
+        NetworkStatsDataMigrationUtils.readPlatformCollection(builder,
+                getInputStreamForResource(R.raw.netstats_uid_v16))
+        // The values are obtained by dumping from NetworkStatsCollection that
+        // read by the logic inside the service.
+        assertValues(builder.build(), 55, 1814302L, 21050L, 31001636L, 26152L)
+    }
+
+    @Test
+    fun testMaybeReadLegacyUid() {
+        val builder = NetworkStatsCollection.Builder(BUCKET_DURATION_MS)
+        NetworkStatsDataMigrationUtils.readLegacyUid(builder,
+                getInputStreamForResource(R.raw.netstats_uid_v4), false /* taggedData */)
+        assertValues(builder.build(), 223, 106245210L, 710722L, 1130647496L, 1103989L)
+    }
+
+    private fun assertValues(
+        collection: NetworkStatsCollection,
+        expectedSize: Int,
+        expectedTxBytes: Long,
+        expectedTxPackets: Long,
+        expectedRxBytes: Long,
+        expectedRxPackets: Long
+    ) {
+        var txBytes = 0L
+        var txPackets = 0L
+        var rxBytes = 0L
+        var rxPackets = 0L
+        val entries = collection.entries
+
+        for (history in entries.values) {
+            for (historyEntry in history.entries) {
+                txBytes += historyEntry.txBytes
+                txPackets += historyEntry.txPackets
+                rxBytes += historyEntry.rxBytes
+                rxPackets += historyEntry.rxPackets
+            }
+        }
+        if (expectedSize != entries.size ||
+                expectedTxBytes != txBytes ||
+                expectedTxPackets != txPackets ||
+                expectedRxBytes != rxBytes ||
+                expectedRxPackets != rxPackets) {
+            fail("expected size=$expectedSize" +
+                    "txb=$expectedTxBytes txp=$expectedTxPackets " +
+                    "rxb=$expectedRxBytes rxp=$expectedRxPackets bus was " +
+                    "size=${entries.size} txb=$txBytes txp=$txPackets " +
+                    "rxb=$rxBytes rxp=$rxPackets")
+        }
+        assertEquals(txBytes + rxBytes, collection.totalBytes)
+    }
+
+    private fun getInputStreamForResource(resourceId: Int): DataInputStream {
+        return DataInputStream(InstrumentationRegistry.getContext()
+                .getResources().openRawResource(resourceId))
+    }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index bdcc21b..84e02ce 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -340,7 +340,7 @@
         inOrder.verifyNoMoreInteractions();
 
         // [2] Start clatd again failed.
-        assertThrows("java.io.IOException: Clatd has started on test0 (pid 10483)",
+        assertThrows("java.io.IOException: Clatd is already running on test0 (pid 10483)",
                 IOException.class,
                 () -> coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX));
 
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 43aeec6..0d34609 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -35,8 +35,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
 import android.content.Context;
-import android.net.NetworkTemplate;
 import android.os.Build;
 import android.os.Looper;
 import android.os.Parcel;
@@ -282,7 +282,7 @@
         // NETWORK_TYPE_5G_NSA.
         setRatTypeForSub(TEST_SUBID1, TelephonyManager.NETWORK_TYPE_LTE,
                 OVERRIDE_NETWORK_TYPE_NR_NSA);
-        assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA);
+        assertRatTypeChangedForSub(TEST_IMSI1, NetworkStatsManager.NETWORK_TYPE_5G_NSA);
         reset(mDelegate);
 
         // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE.
diff --git a/tests/unit/res/raw/netstats_uid_v16 b/tests/unit/res/raw/netstats_uid_v16
new file mode 100644
index 0000000..a6ee430
--- /dev/null
+++ b/tests/unit/res/raw/netstats_uid_v16
Binary files differ