Connectivity metrics: add transports pretty printing

This patch also
  - partially reverts commit f927f0c52e7df5b057e7d28888c3cfed164d241a
    that exposed a getTransports method on NetworkCapabilities.
  - moves enumerateBits to BitUtils (as unpackBits), and adds the
    reverse packBit method.

Bug: 34901696
Test: manually looked at $ adb shell dumpsys connmetrics list
Change-Id: I1650daf8fc9c1b6e0d986d2285f81e888be8847f
Merged-In: Id04f9080e7f75608deeb49306aec34941e71794c

(cherry picked from commit df456e13a1127e3c8594b1d22ea4a9b3dca67a4b)
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 63ccaae..46bb346 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import com.android.internal.util.BitUtils;
 
 /**
  * Represents a core networking event defined in package android.net.metrics.
@@ -78,13 +79,15 @@
     public String toString() {
         StringBuilder buffer = new StringBuilder("ConnectivityMetricsEvent(");
         buffer.append(String.format("%tT.%tL", timestamp, timestamp));
-        // TODO: add transports
         if (netId != 0) {
             buffer.append(", ").append(netId);
         }
         if (ifname != null) {
             buffer.append(", ").append(ifname);
         }
+        for (int t : BitUtils.unpackBits(transports)) {
+            buffer.append(", ").append(NetworkCapabilities.transportNameOf(t));
+        }
         buffer.append("): ").append(data.toString());
         return buffer.toString();
     }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8665b9c..a594bef 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -19,7 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import java.lang.IllegalArgumentException;
+import com.android.internal.util.BitUtils;
 
 /**
  * This class represents the capabilities of a network.  This is used both to specify
@@ -289,7 +289,7 @@
      * @hide
      */
     public int[] getCapabilities() {
-        return enumerateBits(mNetworkCapabilities);
+        return BitUtils.unpackBits(mNetworkCapabilities);
     }
 
     /**
@@ -305,19 +305,6 @@
         return ((mNetworkCapabilities & (1 << capability)) != 0);
     }
 
-    private int[] enumerateBits(long val) {
-        int size = Long.bitCount(val);
-        int[] result = new int[size];
-        int index = 0;
-        int resource = 0;
-        while (val > 0) {
-            if ((val & 1) == 1) result[index++] = resource;
-            val = val >> 1;
-            resource++;
-        }
-        return result;
-    }
-
     private void combineNetCapabilities(NetworkCapabilities nc) {
         this.mNetworkCapabilities |= nc.mNetworkCapabilities;
     }
@@ -428,6 +415,15 @@
     /** @hide */
     public static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE;
 
+    private static final String[] TRANSPORT_NAMES = {
+        "CELLULAR",
+        "WIFI",
+        "BLUETOOTH",
+        "ETHERNET",
+        "VPN",
+        "WIFI_AWARE"
+    };
+
     /**
      * Adds the given transport type to this {@code NetworkCapability} instance.
      * Multiple transports may be applied sequentially.  Note that when searching
@@ -474,18 +470,7 @@
      * @hide
      */
     public int[] getTransportTypes() {
-        return enumerateBits(mTransportTypes);
-    }
-
-    /**
-     * Gets all the transports set on this {@code NetworkCapability} instance.
-     *
-     * @return a bit field composed of up bits at indexes defined by
-     * {@code NetworkCapabilities.TRANSPORT_*} values for this instance.
-     * @hide
-     */
-    public long getTransports() {
-        return mTransportTypes;
+        return BitUtils.unpackBits(mTransportTypes);
     }
 
     /**
@@ -899,18 +884,23 @@
      * @hide
      */
     public static String transportNamesOf(int[] types) {
-        String transports = "";
-        for (int i = 0; i < types.length;) {
-            switch (types[i]) {
-                case TRANSPORT_CELLULAR:    transports += "CELLULAR"; break;
-                case TRANSPORT_WIFI:        transports += "WIFI"; break;
-                case TRANSPORT_BLUETOOTH:   transports += "BLUETOOTH"; break;
-                case TRANSPORT_ETHERNET:    transports += "ETHERNET"; break;
-                case TRANSPORT_VPN:         transports += "VPN"; break;
-                case TRANSPORT_WIFI_AWARE:  transports += "WIFI_AWARE"; break;
-            }
-            if (++i < types.length) transports += "|";
+        if (types == null || types.length == 0) {
+            return "";
         }
-        return transports;
+        StringBuilder transports = new StringBuilder();
+        for (int t : types) {
+            transports.append("|").append(transportNameOf(t));
+        }
+        return transports.substring(1);
+    }
+
+    /**
+     * @hide
+     */
+    public static String transportNameOf(int transport) {
+        if (transport < 0 || TRANSPORT_NAMES.length <= transport) {
+            return "UNKNOWN";
+        }
+        return TRANSPORT_NAMES[transport];
     }
 }
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index ac727ca..4e57efa 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -23,6 +23,7 @@
 import android.os.ServiceManager;
 import android.util.Log;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.BitUtils;
 
 /**
  * Class for logging IpConnectvity events with IpConnectivityMetrics
@@ -117,10 +118,10 @@
      * @param data is a Parcelable instance representing the event.
      * @return true if the event was successfully logged.
      */
-    public boolean log(int netid, long transports, Parcelable data) {
+    public boolean log(int netid, int[] transports, Parcelable data) {
         ConnectivityMetricsEvent ev = makeEv(data);
         ev.netId = netid;
-        ev.transports = transports;
+        ev.transports = BitUtils.packBits(transports);
         return log(ev);
     }
 
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index a208ccb..e349f3d 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -55,4 +55,25 @@
                 && maskedEquals(a.getMostSignificantBits(), b.getMostSignificantBits(),
                     mask.getMostSignificantBits());
     }
+
+    public static int[] unpackBits(long val) {
+        int size = Long.bitCount(val);
+        int[] result = new int[size];
+        int index = 0;
+        int bitPos = 0;
+        while (val > 0) {
+            if ((val & 1) == 1) result[index++] = bitPos;
+            val = val >> 1;
+            bitPos++;
+        }
+        return result;
+    }
+
+    public static long packBits(int[] bits) {
+        long packed = 0;
+        for (int b : bits) {
+            packed |= (1 << b);
+        }
+        return packed;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index d591858..c1a3cc9 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -1048,7 +1048,7 @@
     }
 
     private void logValidationProbe(long durationMs, int probeType, int probeResult) {
-        long transports = mNetworkAgentInfo.networkCapabilities.getTransports();
+        int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
         boolean isFirstValidation = validationStage().isFirstValidation;
         ValidationProbeEvent ev = new ValidationProbeEvent();
         ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation);