Merge "Tethering and Data Saver: There Can Be Only One!" into nyc-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9070ad9..faf5c64 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -81,13 +81,6 @@
 public class ConnectivityManager {
     private static final String TAG = "ConnectivityManager";
 
-    private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
-            new Class[]{ConnectivityManager.class}, new String[]{"CALLBACK_"});
-
-    private static final String whatToString(int what) {
-        return sMagicDecoderRing.get(what, Integer.toString(what));
-    }
-
     /**
      * A change in network connectivity has occurred. A default connection has either
      * been established or lost. The NetworkInfo for the affected network is
@@ -3360,4 +3353,32 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * A holder class for debug info (mapping CALLBACK values to field names). This is stored
+     * in a holder for two reasons:
+     * 1) The reflection necessary to establish the map can't be run at compile-time. Thus, this
+     *    code will make the enclosing class not compile-time initializeable, deferring its
+     *    initialization to zygote startup. This leads to dirty (but shared) memory.
+     *    As this is debug info, use a holder that isn't initialized by default. This way the map
+     *    will be created on demand, while ConnectivityManager can be compile-time initialized.
+     * 2) Static initialization is still preferred for its strong thread safety guarantees without
+     *    requiring a lock.
+     */
+    private static class NoPreloadHolder {
+        public static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
+                new Class[]{ConnectivityManager.class}, new String[]{"CALLBACK_"});
+    }
+
+    static {
+        // When debug is enabled, aggressively initialize the holder by touching the field (which
+        // will guarantee static initialization).
+        if (CallbackHandler.DBG) {
+            Object dummy = NoPreloadHolder.sMagicDecoderRing;
+        }
+    }
+
+    private static final String whatToString(int what) {
+        return NoPreloadHolder.sMagicDecoderRing.get(what, Integer.toString(what));
+    }
 }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 6418614..6243f46 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -809,17 +809,7 @@
     @Override
     public String toString() {
         int[] types = getTransportTypes();
-        String transports = (types.length > 0 ? " 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;
-            }
-            if (++i < types.length) transports += "|";
-        }
+        String transports = (types.length > 0) ? " Transports: " + transportNamesOf(types) : "";
 
         types = getCapabilities();
         String capabilities = (types.length > 0 ? " Capabilities: " : "");
@@ -859,4 +849,22 @@
 
         return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + "]";
     }
+
+    /**
+     * @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;
+            }
+            if (++i < types.length) transports += "|";
+        }
+        return transports;
+    }
 }