Injecting networkstack Quirk stats into statsd

Building/Designing count metrics from server side.

Bug: 151796056
Test: atest NetworkStackIntegrationTest android.net.ip.IpClientIntegrationTest
Change-Id: I780dec84bf41f220051d2a18bb25d8e408c6f5b7
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index b719df2..499cc4c 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -73,6 +73,7 @@
 import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.stats.connectivity.DisconnectCode;
+import android.stats.connectivity.NetworkQuirkEvent;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -98,6 +99,7 @@
 import com.android.networkstack.apishim.common.ShimUtils;
 import com.android.networkstack.arp.ArpPacket;
 import com.android.networkstack.metrics.IpProvisioningMetrics;
+import com.android.networkstack.metrics.NetworkQuirkMetrics;
 import com.android.networkstack.packets.NeighborAdvertisement;
 import com.android.server.NetworkObserverRegistry;
 import com.android.server.NetworkStackService.NetworkStackServiceManager;
@@ -155,6 +157,7 @@
     private final NetworkStackIpMemoryStore mIpMemoryStore;
     private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance();
     private final IpProvisioningMetrics mIpProvisioningMetrics = new IpProvisioningMetrics();
+    private final NetworkQuirkMetrics mNetworkQuirkMetrics;
 
     /**
      * Dump all state machine and connectivity packet logs to the specified writer.
@@ -588,6 +591,10 @@
             return new IpConnectivityLog();
         }
 
+        public NetworkQuirkMetrics getNetworkQuirkMetrics() {
+            return new NetworkQuirkMetrics();
+        }
+
         /**
          * Return whether a feature guarded by a feature flag is enabled.
          * @see NetworkStackUtils#isFeatureEnabled(Context, String, String)
@@ -619,6 +626,7 @@
         mClatInterfaceName = CLAT_PREFIX + ifName;
         mDependencies = deps;
         mMetricsLog = deps.getIpConnectivityLog();
+        mNetworkQuirkMetrics = deps.getNetworkQuirkMetrics();
         mShutdownLatch = new CountDownLatch(1);
         mCm = mContext.getSystemService(ConnectivityManager.class);
         mObserverRegistry = observerRegistry;
@@ -1262,6 +1270,8 @@
             // b/131781810.
             if (newLp.isIpv4Provisioned()) {
                 mInterfaceCtrl.disableIPv6();
+                mNetworkQuirkMetrics.setEvent(NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST);
+                mNetworkQuirkMetrics.statsWrite();
                 mHasDisabledIPv6OnProvLoss = true;
                 delta = PROV_CHANGE_STILL_PROVISIONED;
                 if (DBG) {
diff --git a/src/com/android/networkstack/metrics/NetworkQuirkMetrics.java b/src/com/android/networkstack/metrics/NetworkQuirkMetrics.java
new file mode 100644
index 0000000..dee4504
--- /dev/null
+++ b/src/com/android/networkstack/metrics/NetworkQuirkMetrics.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.metrics;
+
+import android.stats.connectivity.NetworkQuirkEvent;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Class to record the network Quirk event into statsd.
+ * @hide
+ */
+public class NetworkQuirkMetrics {
+    private final Dependencies mDependencies;
+    private final NetworkStackQuirkReported.Builder mStatsBuilder =
+            NetworkStackQuirkReported.newBuilder();
+    /**
+     * Dependencies of {@link NetworkQuirkMetrics}, useful for testing.
+     */
+    @VisibleForTesting
+    public static class Dependencies {
+        /**
+         * @see NetworkStackStatsLog#write.
+         */
+        public void writeStats(int event) {
+            NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_STACK_QUIRK_REPORTED,
+                    0, event);
+        }
+    }
+
+    /**
+     * Get a NetworkQuirkMetrics instance.
+     */
+    public NetworkQuirkMetrics() {
+        this(new Dependencies());
+    }
+
+    @VisibleForTesting
+    public NetworkQuirkMetrics(Dependencies deps) {
+        mDependencies = deps;
+    }
+
+    /**
+     * Write the network Quirk Event into mStatsBuilder.
+     */
+    public void setEvent(NetworkQuirkEvent event) {
+        mStatsBuilder.setEvent(event);
+    }
+
+    /**
+     * Write the NetworkStackQuirkReported proto into statsd.
+     */
+    public NetworkStackQuirkReported statsWrite() {
+        final NetworkStackQuirkReported stats = mStatsBuilder.build();
+        mDependencies.writeStats(stats.getEvent().getNumber());
+        return stats;
+    }
+}
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
index e217e91..c5a8b89 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -128,6 +128,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.stats.connectivity.NetworkQuirkEvent;
 import android.system.ErrnoException;
 import android.system.Os;
 
@@ -147,6 +148,7 @@
 import com.android.networkstack.apishim.common.ShimUtils;
 import com.android.networkstack.arp.ArpPacket;
 import com.android.networkstack.metrics.IpProvisioningMetrics;
+import com.android.networkstack.metrics.NetworkQuirkMetrics;
 import com.android.networkstack.packets.NeighborAdvertisement;
 import com.android.server.NetworkObserver;
 import com.android.server.NetworkObserverRegistry;
@@ -260,6 +262,7 @@
     @Mock private IpMemoryStoreService mIpMemoryStoreService;
     @Mock private PowerManager.WakeLock mTimeoutWakeLock;
     @Mock protected NetworkStackIpMemoryStore mIpMemoryStore;
+    @Mock private NetworkQuirkMetrics.Dependencies mNetworkQuirkMetricsDeps;
 
     @Spy private INetd mNetd;
     private NetworkObserverRegistry mNetworkObserverRegistry;
@@ -448,6 +451,11 @@
         public void setDeviceConfigProperty(String name, int value) {
             mIntConfigProperties.put(name, value);
         }
+
+        @Override
+        public NetworkQuirkMetrics getNetworkQuirkMetrics() {
+            return new NetworkQuirkMetrics(mNetworkQuirkMetricsDeps);
+        }
     }
 
     @NonNull
@@ -2427,6 +2435,11 @@
         assertNotNull(lp);
         assertEquals(lp.getAddresses().get(0), CLIENT_ADDR);
         assertEquals(lp.getDnsServers().get(0), SERVER_ADDR);
+
+        final ArgumentCaptor<Integer> quirkEvent = ArgumentCaptor.forClass(Integer.class);
+        verify(mNetworkQuirkMetricsDeps, timeout(TEST_TIMEOUT_MS)).writeStats(quirkEvent.capture());
+        assertEquals((long) quirkEvent.getValue(),
+                (long) NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST.ordinal());
     }
 
     @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required")