Merge "[CM] Allow timeout in request network" am: 43d964ac8f am: 24c862dad6
am: ac879cbcaf

Change-Id: If42250966d619073d45d608aab454589213b1756
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 4b67aef..14333f7 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -459,6 +459,7 @@
      *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
      */
+    @Deprecated
     public static final int TYPE_MOBILE_MMS  = 2;
     /**
      * A SUPL-specific Mobile data connection.  This network type may use the
@@ -470,6 +471,7 @@
      *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
      */
+    @Deprecated
     public static final int TYPE_MOBILE_SUPL = 3;
     /**
      * A DUN-specific Mobile data connection.  This network type may use the
@@ -487,6 +489,7 @@
      *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport.
      */
+    @Deprecated
     public static final int TYPE_MOBILE_HIPRI = 5;
     /**
      * The WiMAX data connection.  When active, all data traffic
@@ -617,6 +620,7 @@
      * @deprecated All APIs accepting a network type are deprecated. There should be no need to
      *             validate a network type.
      */
+    @Deprecated
     public static boolean isNetworkTypeValid(int networkType) {
         return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
     }
@@ -724,6 +728,7 @@
      *             preference.  Instead we use dynamic network properties of
      *             the networks to describe their precedence.
      */
+    @Deprecated
     public void setNetworkPreference(int preference) {
     }
 
@@ -739,6 +744,7 @@
      *             preference.  Instead we use dynamic network properties of
      *             the networks to describe their precedence.
      */
+    @Deprecated
     public int getNetworkPreference() {
         return TYPE_NONE;
     }
@@ -895,6 +901,7 @@
      *             of the same type. Use {@link #getAllNetworks} and
      *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
+    @Deprecated
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
             return mService.getNetworkInfo(networkType);
@@ -941,6 +948,7 @@
      *             of the same type. Use {@link #getAllNetworks} and
      *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
+    @Deprecated
     public NetworkInfo[] getAllNetworkInfo() {
         try {
             return mService.getAllNetworkInfo();
@@ -961,6 +969,7 @@
      *             of the same type. Use {@link #getAllNetworks} and
      *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
+    @Deprecated
     public Network getNetworkForType(int networkType) {
         try {
             return mService.getNetworkForType(networkType);
@@ -1033,6 +1042,7 @@
      *             {@link #getNetworkInfo(android.net.Network)}, and
      *             {@link #getLinkProperties(android.net.Network)} instead.
      */
+    @Deprecated
     public LinkProperties getLinkProperties(int networkType) {
         try {
             return mService.getLinkPropertiesForType(networkType);
@@ -1114,7 +1124,9 @@
      *             {@link #requestNetwork(NetworkRequest, NetworkCallback)} API.
      *             In {@link VERSION_CODES#M}, and above, this method is unsupported and will
      *             throw {@code UnsupportedOperationException} if called.
+     * @removed
      */
+    @Deprecated
     public int startUsingNetworkFeature(int networkType, String feature) {
         checkLegacyRoutingApiAccess();
         NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -1169,7 +1181,9 @@
      *             {@link #unregisterNetworkCallback(NetworkCallback)} API.
      *             In {@link VERSION_CODES#M}, and above, this method is unsupported and will
      *             throw {@code UnsupportedOperationException} if called.
+     * @removed
      */
+    @Deprecated
     public int stopUsingNetworkFeature(int networkType, String feature) {
         checkLegacyRoutingApiAccess();
         NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -1627,7 +1641,9 @@
      *             {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API.
      *             In {@link VERSION_CODES#M}, and above, this method is unsupported and will
      *             throw {@code UnsupportedOperationException} if called.
+     * @removed
      */
+    @Deprecated
     public boolean requestRouteToHost(int networkType, int hostAddress) {
         return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
     }
@@ -1650,6 +1666,7 @@
      * @deprecated Deprecated in favor of the {@link #requestNetwork} and
      *             {@link #bindProcessToNetwork} API.
      */
+    @Deprecated
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
         checkLegacyRoutingApiAccess();
         try {
@@ -1719,6 +1736,7 @@
      * @hide
      * @deprecated Talk to TelephonyManager directly
      */
+    @Deprecated
     public boolean getMobileDataEnabled() {
         IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
         if (b != null) {
@@ -1882,6 +1900,7 @@
      *               situations where a Context pointer is unavailable.
      * @hide
      */
+    @Deprecated
     static ConnectivityManager getInstanceOrNull() {
         return sInstance;
     }
@@ -1891,6 +1910,7 @@
      *               situations where a Context pointer is unavailable.
      * @hide
      */
+    @Deprecated
     private static ConnectivityManager getInstance() {
         if (getInstanceOrNull() == null) {
             throw new IllegalStateException("No ConnectivityManager yet constructed");
@@ -2298,6 +2318,7 @@
      * @deprecated Use {@link #reportNetworkConnectivity} which allows reporting both
      *             working and non-working connectivity.
      */
+    @Deprecated
     public void reportBadNetwork(Network network) {
         try {
             // One of these will be ignored because it matches system's current state.
@@ -2500,6 +2521,7 @@
      * {@hide}
      * @deprecated Doesn't properly deal with multiple connected networks of the same type.
      */
+    @Deprecated
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
         try {
@@ -3412,6 +3434,75 @@
     }
 
     /**
+     * It is acceptable to briefly use multipath data to provide seamless connectivity for
+     * time-sensitive user-facing operations when the system default network is temporarily
+     * unresponsive. The amount of data should be limited (less than one megabyte), and the
+     * operation should be infrequent to ensure that data usage is limited.
+     *
+     * An example of such an operation might be a time-sensitive foreground activity, such as a
+     * voice command, that the user is performing while walking out of range of a Wi-Fi network.
+     */
+    public static final int MULTIPATH_PREFERENCE_HANDOVER = 1 << 0;
+
+    /**
+     * It is acceptable to use small amounts of multipath data on an ongoing basis to provide
+     * a backup channel for traffic that is primarily going over another network.
+     *
+     * An example might be maintaining backup connections to peers or servers for the purpose of
+     * fast fallback if the default network is temporarily unresponsive or disconnects. The traffic
+     * on backup paths should be negligible compared to the traffic on the main path.
+     */
+    public static final int MULTIPATH_PREFERENCE_RELIABILITY = 1 << 1;
+
+    /**
+     * It is acceptable to use metered data to improve network latency and performance.
+     */
+    public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 1 << 2;
+
+    /**
+     * Return value to use for unmetered networks. On such networks we currently set all the flags
+     * to true.
+     * @hide
+     */
+    public static final int MULTIPATH_PREFERENCE_UNMETERED =
+            MULTIPATH_PREFERENCE_HANDOVER |
+            MULTIPATH_PREFERENCE_RELIABILITY |
+            MULTIPATH_PREFERENCE_PERFORMANCE;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+            MULTIPATH_PREFERENCE_HANDOVER,
+            MULTIPATH_PREFERENCE_RELIABILITY,
+            MULTIPATH_PREFERENCE_PERFORMANCE,
+    })
+    public @interface MultipathPreference {
+    }
+
+    /**
+     * Provides a hint to the calling application on whether it is desirable to use the
+     * multinetwork APIs (e.g., {@link Network#openConnection}, {@link Network#bindSocket}, etc.)
+     * for multipath data transfer on this network when it is not the system default network.
+     * Applications desiring to use multipath network protocols should call this method before
+     * each such operation.
+     * <p>
+     * This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @param network The network on which the application desires to use multipath data.
+     *                If {@code null}, this method will return the a preference that will generally
+     *                apply to metered networks.
+     * @return a bitwise OR of zero or more of the  {@code MULTIPATH_PREFERENCE_*} constants.
+     */
+    public @MultipathPreference int getMultipathPreference(Network network) {
+        try {
+            return mService.getMultipathPreference(network);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Resets all connectivity manager settings back to factory defaults.
      * @hide
      */
@@ -3468,6 +3559,7 @@
      *             {@link #bindProcessToNetwork} instead.  {@code bindProcessToNetwork}
      *             is a direct replacement.
      */
+    @Deprecated
     public static boolean setProcessDefaultNetwork(Network network) {
         int netId = (network == null) ? NETID_UNSET : network.netId;
         if (netId == NetworkUtils.getBoundNetworkForProcess()) {
@@ -3514,6 +3606,7 @@
      *             {@link IllegalStateException}.  Use {@link #getBoundNetworkForProcess} instead.
      *             {@code getBoundNetworkForProcess} is a direct replacement.
      */
+    @Deprecated
     public static Network getProcessDefaultNetwork() {
         int netId = NetworkUtils.getBoundNetworkForProcess();
         if (netId == NETID_UNSET) return null;
@@ -3558,6 +3651,7 @@
      * @hide
      * @deprecated This is strictly for legacy usage to support {@link #startUsingNetworkFeature}.
      */
+    @Deprecated
     public static boolean setProcessDefaultNetworkForHostResolution(Network network) {
         return NetworkUtils.bindProcessToNetworkForHostResolution(
                 network == null ? NETID_UNSET : network.netId);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index b123c28..425e494 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -161,6 +161,8 @@
     void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
     void setAvoidUnvalidated(in Network network);
 
+    int getMultipathPreference(in Network Network);
+
     int getRestoreDefaultNetworkDelay(int networkType);
 
     boolean addVpnAddress(String address, int prefixLength);
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index a677d73..fe9563d 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -92,6 +92,7 @@
      *
      * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
      */
+    @Deprecated
     public native static boolean bindProcessToNetworkForHostResolution(int netId);
 
     /**
diff --git a/core/tests/coretests/src/android/net/IpPrefixTest.java b/core/tests/coretests/src/android/net/IpPrefixTest.java
index fcc6389..4f2387d 100644
--- a/core/tests/coretests/src/android/net/IpPrefixTest.java
+++ b/core/tests/coretests/src/android/net/IpPrefixTest.java
@@ -18,14 +18,14 @@
 
 import android.net.IpPrefix;
 import android.os.Parcel;
-import static android.test.MoreAsserts.assertNotEqual;
 import android.test.suitebuilder.annotation.SmallTest;
-
-import static org.junit.Assert.assertArrayEquals;
 import java.net.InetAddress;
 import java.util.Random;
 import junit.framework.TestCase;
 
+import static android.test.MoreAsserts.assertNotEqual;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
 
 public class IpPrefixTest extends TestCase {
 
@@ -242,25 +242,42 @@
 
     @SmallTest
     public void testHashCode() {
-        IpPrefix p;
-        int oldCode = -1;
+        IpPrefix p = new IpPrefix(new byte[4], 0);
         Random random = new Random();
         for (int i = 0; i < 100; i++) {
+            final IpPrefix oldP = p;
             if (random.nextBoolean()) {
                 // IPv4.
                 byte[] b = new byte[4];
                 random.nextBytes(b);
                 p = new IpPrefix(b, random.nextInt(33));
-                assertNotEqual(oldCode, p.hashCode());
-                oldCode = p.hashCode();
             } else {
                 // IPv6.
                 byte[] b = new byte[16];
                 random.nextBytes(b);
                 p = new IpPrefix(b, random.nextInt(129));
-                assertNotEqual(oldCode, p.hashCode());
-                oldCode = p.hashCode();
             }
+            if (p.equals(oldP)) {
+              assertEquals(p.hashCode(), oldP.hashCode());
+            }
+            if (p.hashCode() != oldP.hashCode()) {
+              assertNotEqual(p, oldP);
+            }
+        }
+    }
+
+    @SmallTest
+    public void testHashCodeIsNotConstant() {
+        IpPrefix[] prefixes = {
+            new IpPrefix("2001:db8:f00::ace:d00d/127"),
+            new IpPrefix("192.0.2.0/23"),
+            new IpPrefix("::/0"),
+            new IpPrefix("0.0.0.0/0"),
+        };
+        for (int i = 0; i < prefixes.length; i++) {
+          for (int j = i + 1; j < prefixes.length; j++) {
+            assertNotEqual(prefixes[i].hashCode(), prefixes[j].hashCode());
+          }
         }
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f58cdba..16ed276 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1536,19 +1536,10 @@
         }
 
         @Override
-        public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) {
+        public void onUidPoliciesChanged(int uid, int uidPolicies) {
+            // caller is NPMS, since we only register with them
             if (LOGD_RULES) {
-                // caller is NPMS, since we only register with them
-                log("onRestrictBackgroundWhitelistChanged(uid=" + uid + ", whitelisted="
-                        + whitelisted + ")");
-            }
-        }
-        @Override
-        public void onRestrictBackgroundBlacklistChanged(int uid, boolean blacklisted) {
-            if (LOGD_RULES) {
-                // caller is NPMS, since we only register with them
-                log("onRestrictBackgroundBlacklistChanged(uid=" + uid + ", blacklisted="
-                        + blacklisted + ")");
+                log("onUidRulesChanged(uid=" + uid + ", uidPolicies=" + uidPolicies + ")");
             }
         }
     };
@@ -2855,6 +2846,18 @@
         }
     }
 
+    @Override
+    public int getMultipathPreference(Network network) {
+        enforceAccessPermission();
+
+        NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai != null && !nai.networkInfo.isMetered()) {
+            return ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED;
+        }
+
+        return mMultinetworkPolicyTracker.getMeteredMultipathPreference();
+    }
+
     private class InternalHandler extends Handler {
         public InternalHandler(Looper looper) {
             super(looper);
@@ -4555,9 +4558,28 @@
         } catch (Exception e) {
             loge("Exception in setDnsConfigurationForNetwork: " + e);
         }
+        final NetworkAgentInfo defaultNai = getDefaultNetwork();
+        if (defaultNai != null && defaultNai.network.netId == netId) {
+            setDefaultDnsSystemProperties(dnses);
+        }
         flushVmDnsCache();
     }
 
+    private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
+        int last = 0;
+        for (InetAddress dns : dnses) {
+            ++last;
+            String key = "net.dns" + last;
+            String value = dns.getHostAddress();
+            SystemProperties.set(key, value);
+        }
+        for (int i = last + 1; i <= mNumDnsEntries; ++i) {
+            String key = "net.dns" + i;
+            SystemProperties.set(key, "");
+        }
+        mNumDnsEntries = last;
+    }
+
     private String getNetworkPermission(NetworkCapabilities nc) {
         // TODO: make these permission strings AIDL constants instead.
         if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
@@ -4774,6 +4796,7 @@
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
         updateTcpBufferSizes(newNetwork);
+        setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
     }
 
     private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 5f9efe7..85d1d1e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -23,6 +23,7 @@
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.TrafficStats;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -381,7 +382,12 @@
         protected void setupSocket(
                 int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
                 throws ErrnoException, IOException {
-            mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
+            final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
+            try {
+                mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
+            } finally {
+                TrafficStats.setThreadStatsTag(oldTag);
+            }
             // Setting SNDTIMEO is purely for defensive purposes.
             Os.setsockoptTimeval(mFileDescriptor,
                     SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(writeTimeout));
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 4ff6657..83751a9 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -31,7 +31,8 @@
 import android.widget.Toast;
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.notification.SystemNotificationChannels;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -187,7 +188,9 @@
             return;
         }
 
-        Notification.Builder builder = new Notification.Builder(mContext)
+        final String channelId = highPriority ? SystemNotificationChannels.NETWORK_ALERTS :
+                SystemNotificationChannels.NETWORK_STATUS;
+        Notification.Builder builder = new Notification.Builder(mContext, channelId)
                 .setWhen(System.currentTimeMillis())
                 .setShowWhen(notifyType == NotificationType.NETWORK_SWITCH)
                 .setSmallIcon(icon)
@@ -198,10 +201,6 @@
                 .setContentTitle(title)
                 .setContentIntent(intent)
                 .setLocalOnly(true)
-                .setPriority(highPriority ?
-                        Notification.PRIORITY_HIGH :
-                        Notification.PRIORITY_DEFAULT)
-                .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
                 .setOnlyAlertOnce(true);
 
         if (notifyType == NotificationType.NETWORK_SWITCH) {
diff --git a/services/net/java/android/net/util/MultinetworkPolicyTracker.java b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
index ebd131b..424e40d 100644
--- a/services/net/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.net.ConnectivityManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
@@ -29,10 +30,14 @@
 import android.provider.Settings;
 import android.util.Slog;
 
+import java.util.Arrays;
+import java.util.List;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.R;
 
 import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
+import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
 
 /**
  * A class to encapsulate management of the "Smart Networking" capability of
@@ -57,12 +62,13 @@
     private final Context mContext;
     private final Handler mHandler;
     private final Runnable mReevaluateRunnable;
-    private final Uri mAvoidBadWifiUri;
+    private final List<Uri> mSettingsUris;
     private final ContentResolver mResolver;
     private final SettingObserver mSettingObserver;
     private final BroadcastReceiver mBroadcastReceiver;
 
     private volatile boolean mAvoidBadWifi = true;
+    private volatile int mMeteredMultipathPreference;
 
     public MultinetworkPolicyTracker(Context ctx, Handler handler) {
         this(ctx, handler, null);
@@ -72,9 +78,14 @@
         mContext = ctx;
         mHandler = handler;
         mReevaluateRunnable = () -> {
-            if (updateAvoidBadWifi() && avoidBadWifiCallback != null) avoidBadWifiCallback.run();
+            if (updateAvoidBadWifi() && avoidBadWifiCallback != null) {
+                avoidBadWifiCallback.run();
+            }
+            updateMeteredMultipathPreference();
         };
-        mAvoidBadWifiUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI);
+        mSettingsUris = Arrays.asList(
+            Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
+            Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
         mResolver = mContext.getContentResolver();
         mSettingObserver = new SettingObserver();
         mBroadcastReceiver = new BroadcastReceiver() {
@@ -85,10 +96,13 @@
         };
 
         updateAvoidBadWifi();
+        updateMeteredMultipathPreference();
     }
 
     public void start() {
-        mResolver.registerContentObserver(mAvoidBadWifiUri, false, mSettingObserver);
+        for (Uri uri : mSettingsUris) {
+            mResolver.registerContentObserver(uri, false, mSettingObserver);
+        }
 
         final IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
@@ -108,6 +122,10 @@
         return mAvoidBadWifi;
     }
 
+    public int getMeteredMultipathPreference() {
+        return mMeteredMultipathPreference;
+    }
+
     /**
      * Whether the device or carrier configuration disables avoiding bad wifi by default.
      */
@@ -138,6 +156,23 @@
         return mAvoidBadWifi != prev;
     }
 
+    /**
+     * The default (device and carrier-dependent) value for metered multipath preference.
+     */
+    public int configMeteredMultipathPreference() {
+        return mContext.getResources().getInteger(
+                R.integer.config_networkMeteredMultipathPreference);
+    }
+
+    public void updateMeteredMultipathPreference() {
+        String setting = Settings.Global.getString(mResolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
+        try {
+            mMeteredMultipathPreference = Integer.parseInt(setting);
+        } catch (NumberFormatException e) {
+            mMeteredMultipathPreference = configMeteredMultipathPreference();
+        }
+    }
+
     private class SettingObserver extends ContentObserver {
         public SettingObserver() {
             super(null);
@@ -150,7 +185,9 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            if (!mAvoidBadWifiUri.equals(uri)) return;
+            if (!mSettingsUris.contains(uri)) {
+                Slog.wtf(TAG, "Unexpected settings observation: " + uri);
+            }
             reevaluate();
         }
     }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b033382..04443a5 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -600,6 +600,7 @@
 
     private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
         public volatile boolean configRestrictsAvoidBadWifi;
+        public volatile int configMeteredMultipathPreference;
 
         public WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
             super(c, h, r);
@@ -609,6 +610,11 @@
         public boolean configRestrictsAvoidBadWifi() {
             return configRestrictsAvoidBadWifi;
         }
+
+        @Override
+        public int configMeteredMultipathPreference() {
+            return configMeteredMultipathPreference;
+        }
     }
 
     private class WrappedConnectivityService extends ConnectivityService {
@@ -2349,6 +2355,26 @@
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
+    @SmallTest
+    public void testMeteredMultipathPreferenceSetting() throws Exception {
+        final ContentResolver cr = mServiceContext.getContentResolver();
+        final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
+        final String settingName = Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+
+        for (int config : Arrays.asList(0, 3, 2)) {
+            for (String setting: Arrays.asList(null, "0", "2", "1")) {
+                tracker.configMeteredMultipathPreference = config;
+                Settings.Global.putString(cr, settingName, setting);
+                tracker.reevaluate();
+                mService.waitForIdle();
+
+                final int expected = (setting != null) ? Integer.parseInt(setting) : config;
+                String msg = String.format("config=%d, setting=%s", config, setting);
+                assertEquals(msg, expected, mCm.getMultipathPreference(null));
+            }
+        }
+    }
+
     /**
      * Validate that a satisfied network request does not trigger onUnavailable() once the
      * time-out period expires.
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 011e505..415911e 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -25,7 +25,7 @@
 import static com.android.server.connectivity.MetricsTestUtil.anIntArray;
 import static com.android.server.connectivity.MetricsTestUtil.b;
 import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent;
-import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityLog;
+import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
 
 import android.net.ConnectivityMetricsEvent;
 import android.net.metrics.ApfProgramEvent;
@@ -58,8 +58,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  default_network_event <",
                 "    network_id <",
                 "      network_id: 102",
@@ -89,8 +89,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  dhcp_event <",
                 "    duration_ms: 192",
                 "    if_name: \"wlan0\"",
@@ -112,8 +112,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  dhcp_event <",
                 "    duration_ms: 0",
                 "    if_name: \"wlan0\"",
@@ -137,8 +137,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  dns_lookup_batch <",
                 "    event_types: 1",
                 "    event_types: 1",
@@ -185,8 +185,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  ip_provisioning_event <",
                 "    event_type: 1",
                 "    if_name: \"wlan0\"",
@@ -208,8 +208,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  ip_reachability_event <",
                 "    event_type: 512",
                 "    if_name: \"wlan0\"",
@@ -231,8 +231,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  network_event <",
                 "    event_type: 5",
                 "    latency_ms: 20410",
@@ -258,8 +258,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  validation_probe_event <",
                 "    latency_ms: 40730",
                 "    network_id <",
@@ -287,8 +287,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  apf_program_event <",
                 "    current_ras: 9",
                 "    drop_multicast: true",
@@ -319,8 +319,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  apf_statistics <",
                 "    dropped_ras: 2",
                 "    duration_ms: 45000",
@@ -351,8 +351,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  ra_event <",
                 "    dnssl_lifetime: -1",
                 "    prefix_preferred_lifetime: 300",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 450653c..f56f3f8 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -34,7 +34,7 @@
 import android.os.Parcelable;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
-import com.android.server.connectivity.metrics.IpConnectivityLogClass;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Collections;
@@ -158,16 +158,16 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 100",
-                "  transport: 0",
                 "  ip_reachability_event <",
                 "    event_type: 512",
                 "    if_name: \"wlan0\"",
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 200",
-                "  transport: 0",
                 "  dhcp_event <",
                 "    duration_ms: 192",
                 "    if_name: \"wlan0\"",
@@ -175,8 +175,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 300",
-                "  transport: 0",
                 "  default_network_event <",
                 "    network_id <",
                 "      network_id: 102",
@@ -191,8 +191,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 400",
-                "  transport: 0",
                 "  ip_provisioning_event <",
                 "    event_type: 1",
                 "    if_name: \"wlan0\"",
@@ -200,8 +200,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 500",
-                "  transport: 0",
                 "  validation_probe_event <",
                 "    latency_ms: 40730",
                 "    network_id <",
@@ -212,8 +212,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 600",
-                "  transport: 0",
                 "  apf_statistics <",
                 "    dropped_ras: 2",
                 "    duration_ms: 45000",
@@ -226,8 +226,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 700",
-                "  transport: 0",
                 "  ra_event <",
                 "    dnssl_lifetime: -1",
                 "    prefix_preferred_lifetime: 300",
diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
index e201012..c5965e8 100644
--- a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
+++ b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
@@ -17,101 +17,59 @@
 package com.android.server.connectivity;
 
 import android.net.ConnectivityMetricsEvent;
-import android.net.ConnectivityMetricsLogger;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.function.Consumer;
+
 abstract public class MetricsTestUtil {
     private MetricsTestUtil() {
     }
 
-    static ConnectivityMetricsEvent ipEv(Parcelable p) {
-        return ev(ConnectivityMetricsLogger.COMPONENT_TAG_CONNECTIVITY, p);
+    static ConnectivityMetricsEvent ev(Parcelable p) {
+        return new ConnectivityMetricsEvent(1L, 0, 0, p);
     }
 
-    static ConnectivityMetricsEvent telephonyEv() {
-        return ev(ConnectivityMetricsLogger.COMPONENT_TAG_TELEPHONY, new Bundle());
-    }
-
-    static ConnectivityMetricsEvent ev(int tag, Parcelable p) {
-        return new ConnectivityMetricsEvent(1L, tag, 0, p);
-    }
-
-    // Utiliy interface for describing the content of a Parcel. This relies on
-    // the implementation defails of Parcelable and on the fact that the fully
-    // qualified Parcelable class names are written as string in the Parcels.
-    interface ParcelField {
-        void write(Parcel p);
-    }
-
-    static ConnectivityMetricsEvent describeIpEvent(ParcelField... fs) {
+    static ConnectivityMetricsEvent describeIpEvent(Consumer<Parcel>... fs) {
         Parcel p = Parcel.obtain();
-        for (ParcelField f : fs) {
-            f.write(p);
+        for (Consumer<Parcel> f : fs) {
+            f.accept(p);
         }
         p.setDataPosition(0);
-        return ipEv(p.readParcelable(ClassLoader.getSystemClassLoader()));
+        return ev(p.readParcelable(ClassLoader.getSystemClassLoader()));
     }
 
-    static ParcelField aType(Class<?> c) {
-        return new ParcelField() {
-            public void write(Parcel p) {
-                p.writeString(c.getName());
-            }
-        };
+    static Consumer<Parcel> aType(Class<?> c) {
+        return aString(c.getName());
     }
 
-    static ParcelField aBool(boolean b) {
+    static Consumer<Parcel> aBool(boolean b) {
         return aByte((byte) (b ? 1 : 0));
     }
 
-    static ParcelField aByte(byte b) {
-        return new ParcelField() {
-            public void write(Parcel p) {
-                p.writeByte(b);
-            }
-        };
+    static Consumer<Parcel> aByte(byte b) {
+        return (p) -> p.writeByte(b);
     }
 
-    static ParcelField anInt(int i) {
-        return new ParcelField() {
-            public void write(Parcel p) {
-                p.writeInt(i);
-            }
-        };
+    static Consumer<Parcel> anInt(int i) {
+        return (p) -> p.writeInt(i);
     }
 
-    static ParcelField aLong(long l) {
-        return new ParcelField() {
-            public void write(Parcel p) {
-                p.writeLong(l);
-            }
-        };
+    static Consumer<Parcel> aLong(long l) {
+        return (p) -> p.writeLong(l);
     }
 
-    static ParcelField aString(String s) {
-        return new ParcelField() {
-            public void write(Parcel p) {
-                p.writeString(s);
-            }
-        };
+    static Consumer<Parcel> aString(String s) {
+        return (p) -> p.writeString(s);
     }
 
-    static ParcelField aByteArray(byte... ary) {
-        return new ParcelField() {
-            public void write(Parcel p) {
-                p.writeByteArray(ary);
-            }
-        };
+    static Consumer<Parcel> aByteArray(byte... ary) {
+        return (p) -> p.writeByteArray(ary);
     }
 
-    static ParcelField anIntArray(int... ary) {
-        return new ParcelField() {
-            public void write(Parcel p) {
-                p.writeIntArray(ary);
-            }
-        };
+    static Consumer<Parcel> anIntArray(int... ary) {
+        return (p) -> p.writeIntArray(ary);
     }
 
     static byte b(int i) {
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 97afa60..cfd5598 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -19,13 +19,14 @@
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
+import android.net.metrics.ConnectStats;
 import android.net.metrics.DnsEvent;
 import android.net.metrics.INetdEventListener;
 import android.net.metrics.IpConnectivityLog;
 import android.os.RemoteException;
 import android.system.OsConstants;
 import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
 import java.io.FileOutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -211,9 +212,9 @@
         mNetdEventListenerService.flushStatistics(events);
 
         IpConnectivityEvent got = events.get(0);
-        String want = joinLines(
+        String want = String.join("\n",
+                "link_layer: 0",
                 "time_ms: 0",
-                "transport: 0",
                 "connect_statistics <",
                 "  connect_count: 12",
                 "  errnos_counters <",
@@ -247,7 +248,8 @@
                 "  latencies_ms: 67",
                 "  latencies_ms: 110",
                 "  latencies_ms: 214",
-                "  latencies_ms: 523");
+                "  latencies_ms: 523",
+                ">\n");
         verifyConnectEvent(want, got);
     }
 
@@ -305,18 +307,10 @@
                 && Arrays.equals(expected.latenciesMs, got.latenciesMs));
     }
 
-    static String joinLines(String ... elems) {
-        StringBuilder b = new StringBuilder();
-        for (String s : elems) {
-            b.append(s).append("\n");
-        }
-        return b.toString();
-    }
-
     static void verifyConnectEvent(String expected, IpConnectivityEvent got) {
         try {
-            Arrays.sort(got.connectStatistics.latenciesMs);
-            Arrays.sort(got.connectStatistics.errnosCounters,
+            Arrays.sort(got.getConnectStatistics().latenciesMs);
+            Arrays.sort(got.getConnectStatistics().errnosCounters,
                     Comparator.comparingInt((p) -> p.key));
             assertEquals(expected, got.toString());
         } catch (Exception e) {
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/connectivity/NetworkNotificationManagerTest.java
rename to tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java