Merge "Disable mDefaultDns to see if we can remove it." into mnc-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 26878c0..3a3c47d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -611,11 +611,11 @@
 
     /**
      * Retrieves the current preferred network type.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return an integer representing the preferred network type
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * @deprecated Functionality has been removed as it no longer makes sense,
      *             with many more than two networks - we'd need an array to express
      *             preference.  Instead we use dynamic network properties of
@@ -631,12 +631,11 @@
      * You should always check {@link NetworkInfo#isConnected()} before initiating
      * network traffic. This may return {@code null} when there is no default
      * network.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        or {@code null} if no default network is currently active
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      */
     public NetworkInfo getActiveNetworkInfo() {
         try {
@@ -652,12 +651,11 @@
      * network disconnects, the returned {@code Network} object will no longer
      * be usable.  This will return {@code null} when there is no default
      * network.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return a {@link Network} object for the current default network or
      *        {@code null} if no default network is currently active
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      */
     public Network getActiveNetwork() {
         try {
@@ -671,13 +669,13 @@
      * Returns details about the currently active default data network
      * for a given uid.  This is for internal use only to avoid spying
      * other apps.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        for the given uid or {@code null} if no default network is
      *        available for the specified uid.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}
      * {@hide}
      */
     public NetworkInfo getActiveNetworkInfoForUid(int uid) {
@@ -691,6 +689,8 @@
     /**
      * Returns connection status information about a particular
      * network type.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @param networkType integer specifying which networkType in
      *        which you're interested.
@@ -698,9 +698,6 @@
      *        network type or {@code null} if the type is not
      *        supported by the device.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
-     *
      * @deprecated This method does not support multiple connected networks
      *             of the same type. Use {@link #getAllNetworks} and
      *             {@link #getNetworkInfo(android.net.Network)} instead.
@@ -716,15 +713,14 @@
     /**
      * Returns connection status information about a particular
      * Network.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @param network {@link Network} specifying which network
      *        in which you're interested.
      * @return a {@link NetworkInfo} object for the requested
      *        network or {@code null} if the {@code Network}
      *        is not valid.
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      */
     public NetworkInfo getNetworkInfo(Network network) {
         try {
@@ -737,13 +733,12 @@
     /**
      * Returns connection status information about all network
      * types supported by the device.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return an array of {@link NetworkInfo} objects.  Check each
      * {@link NetworkInfo#getType} for which type each applies.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
-     *
      * @deprecated This method does not support multiple connected networks
      *             of the same type. Use {@link #getAllNetworks} and
      *             {@link #getNetworkInfo(android.net.Network)} instead.
@@ -779,11 +774,10 @@
     /**
      * Returns an array of all {@link Network} currently tracked by the
      * framework.
-     *
-     * @return an array of {@link Network} objects.
-     *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @return an array of {@link Network} objects.
      */
     public Network[] getAllNetworks() {
         try {
@@ -812,13 +806,12 @@
      * You should always check {@link NetworkInfo#isConnected()} before initiating
      * network traffic. This may return {@code null} when there is no default
      * network.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        or {@code null} if no default network is currently active
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
-     *
      * {@hide}
      */
     public NetworkInfo getProvisioningOrActiveNetworkInfo() {
@@ -831,13 +824,13 @@
 
     /**
      * Returns the IP information for the current default network.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return a {@link LinkProperties} object describing the IP info
      *        for the current default network, or {@code null} if there
      *        is no current default network.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public LinkProperties getActiveLinkProperties() {
@@ -850,14 +843,14 @@
 
     /**
      * Returns the IP information for a given network type.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @param networkType the network type of interest.
      * @return a {@link LinkProperties} object describing the IP info
      *        for the given networkType, or {@code null} if there is
      *        no current default network.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      * @deprecated This method does not support multiple connected networks
      *             of the same type. Use {@link #getAllNetworks},
@@ -875,10 +868,12 @@
     /**
      * Get the {@link LinkProperties} for the given {@link Network}.  This
      * will return {@code null} if the network is unknown.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @param network The {@link Network} object identifying the network in question.
      * @return The {@link LinkProperties} for the network, or {@code null}.
-     **/
+     */
     public LinkProperties getLinkProperties(Network network) {
         try {
             return mService.getLinkProperties(network);
@@ -890,6 +885,8 @@
     /**
      * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}.  This
      * will return {@code null} if the network is unknown.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @param network The {@link Network} object identifying the network in question.
      * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
@@ -1493,11 +1490,11 @@
     /**
      * Get the set of tetherable, available interfaces.  This list is limited by
      * device configuration and current interface existence.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return an array of 0 or more Strings of tetherable interface names.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public String[] getTetherableIfaces() {
@@ -1510,11 +1507,11 @@
 
     /**
      * Get the set of tethered interfaces.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return an array of 0 or more String of currently tethered interface names.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public String[] getTetheredIfaces() {
@@ -1532,12 +1529,12 @@
      * may cause them to reset to the available state.
      * {@link ConnectivityManager#getLastTetherError} can be used to get more
      * information on the cause of the errors.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return an array of 0 or more String indicating the interface names
      *        which failed to tether.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public String[] getTetheringErroredIfaces() {
@@ -1570,12 +1567,12 @@
      * allowed between the tethered devices and this device, though upstream net
      * access will of course fail until an upstream network interface becomes
      * active.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      *
      * @param iface the interface name to tether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * {@hide}
      */
     public int tether(String iface) {
@@ -1588,12 +1585,12 @@
 
     /**
      * Stop tethering the named interface.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      *
      * @param iface the interface name to untether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * {@hide}
      */
     public int untether(String iface) {
@@ -1608,11 +1605,11 @@
      * Check if the device allows for tethering.  It may be disabled via
      * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
      * due to device configuration.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return a boolean - {@code true} indicating Tethering is supported.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public boolean isTetheringSupported() {
@@ -1627,12 +1624,12 @@
      * Get the list of regular expressions that define any tetherable
      * USB network interfaces.  If USB tethering is not supported by the
      * device, this list should be empty.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return an array of 0 or more regular expression Strings defining
      *        what interfaces are considered tetherable usb interfaces.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public String[] getTetherableUsbRegexs() {
@@ -1647,12 +1644,12 @@
      * Get the list of regular expressions that define any tetherable
      * Wifi network interfaces.  If Wifi tethering is not supported by the
      * device, this list should be empty.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return an array of 0 or more regular expression Strings defining
      *        what interfaces are considered tetherable wifi interfaces.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public String[] getTetherableWifiRegexs() {
@@ -1667,12 +1664,12 @@
      * Get the list of regular expressions that define any tetherable
      * Bluetooth network interfaces.  If Bluetooth tethering is not supported by the
      * device, this list should be empty.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return an array of 0 or more regular expression Strings defining
      *        what interfaces are considered tetherable bluetooth interfaces.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public String[] getTetherableBluetoothRegexs() {
@@ -1689,12 +1686,12 @@
      * attempt to switch to Rndis and subsequently tether the resulting
      * interface on {@code true} or turn off tethering and switch off
      * Rndis on {@code false}.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      *
      * @param enable a boolean - {@code true} to enable tethering
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * {@hide}
      */
     public int setUsbTethering(boolean enable) {
@@ -1731,13 +1728,13 @@
     /**
      * Get a more detailed error code after a Tethering or Untethering
      * request asynchronously failed.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @param iface The name of the interface of interest
      * @return error The error code of the last error tethering or untethering the named
      *               interface
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
     public int getLastTetherError(String iface) {
@@ -1751,12 +1748,11 @@
     /**
      * Report network connectivity status.  This is currently used only
      * to alter status bar UI.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#STATUS_BAR}.
      *
      * @param networkType The type of network you want to report on
      * @param percentage The quality of the connection 0 is bad, 100 is good
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#STATUS_BAR}.
      * {@hide}
      */
     public void reportInetCondition(int networkType, int percentage) {
@@ -1886,12 +1882,11 @@
      * for typical HTTP proxies - they are general network dependent.  However if you're
      * doing something unusual like general internal filtering this may be useful.  On
      * a private network where the proxy is not accessible, you may break HTTP using this.
-     *
-     * @param p The a {@link ProxyInfo} object defining the new global
-     *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
-     *
      * <p>This method requires the caller to hold the permission
      * android.Manifest.permission#CONNECTIVITY_INTERNAL.
+     *
+     * @param p A {@link ProxyInfo} object defining the new global
+     *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
      * @hide
      */
     public void setGlobalProxy(ProxyInfo p) {
@@ -1906,9 +1901,6 @@
      *
      * @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
      *        if no global HTTP proxy is set.
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * @hide
      */
     public ProxyInfo getGlobalProxy() {
@@ -1920,6 +1912,28 @@
     }
 
     /**
+     * Retrieve the global HTTP proxy, or if no global HTTP proxy is set, a
+     * network-specific HTTP proxy.  If {@code network} is null, the
+     * network-specific proxy returned is the proxy of the default active
+     * network.
+     *
+     * @return {@link ProxyInfo} for the current global HTTP proxy, or if no
+     *         global HTTP proxy is set, {@code ProxyInfo} for {@code network},
+     *         or when {@code network} is {@code null},
+     *         the {@code ProxyInfo} for the default active network.  Returns
+     *         {@code null} when no proxy applies or the caller doesn't have
+     *         permission to use {@code network}.
+     * @hide
+     */
+    public ProxyInfo getProxyForNetwork(Network network) {
+        try {
+            return mService.getProxyForNetwork(network);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Get the current default HTTP proxy settings.  If a global proxy is set it will be returned,
      * otherwise if this process is bound to a {@link Network} using
      * {@link #bindProcessToNetwork} then that {@code Network}'s proxy is returned, otherwise
@@ -1929,19 +1943,7 @@
      *        HTTP proxy is active.
      */
     public ProxyInfo getDefaultProxy() {
-        final Network network = getBoundNetworkForProcess();
-        if (network != null) {
-            final ProxyInfo globalProxy = getGlobalProxy();
-            if (globalProxy != null) return globalProxy;
-            final LinkProperties lp = getLinkProperties(network);
-            if (lp != null) return lp.getHttpProxy();
-            return null;
-        }
-        try {
-            return mService.getDefaultProxy();
-        } catch (RemoteException e) {
-            return null;
-        }
+        return getProxyForNetwork(getBoundNetworkForProcess());
     }
 
     /**
@@ -1951,12 +1953,12 @@
      * hardware supports it.  For example a GSM phone without a SIM
      * should still return {@code true} for mobile data, but a wifi only
      * tablet would return {@code false}.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @param networkType The network type we'd like to check
      * @return {@code true} if supported, else {@code false}
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * @hide
      */
     public boolean isNetworkSupported(int networkType) {
@@ -1973,12 +1975,11 @@
      * battery/performance issues. You should check this before doing large
      * data transfers, and warn the user or delay the operation until another
      * network is available.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @return {@code true} if large transfers should be avoided, otherwise
      *        {@code false}.
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      */
     public boolean isActiveNetworkMetered() {
         try {
@@ -2008,13 +2009,12 @@
     /**
      * Signal that the captive portal check on the indicated network
      * is complete and whether its a captive portal or not.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
      *
      * @param info the {@link NetworkInfo} object for the networkType
      *        in question.
      * @param isCaptivePortal true/false.
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
      * {@hide}
      */
     public void captivePortalCheckCompleted(NetworkInfo info, boolean isCaptivePortal) {
@@ -2086,11 +2086,11 @@
 
     /**
      * Set the value for enabling/disabling airplane mode
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
      *
      * @param enable whether to enable airplane mode or not
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
      * @hide
      */
     public void setAirplaneMode(boolean enable) {
@@ -2431,6 +2431,8 @@
      * Status of the request can be followed by listening to the various
      * callbacks described in {@link NetworkCallback}.  The {@link Network}
      * can be used to direct traffic to the network.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The {@link NetworkCallback} to be utilized for this
@@ -2450,6 +2452,8 @@
      * network is not found within the given time (in milliseconds) the
      * {@link NetworkCallback#unavailable} callback is called.  The request must
      * still be released normally by calling {@link releaseNetworkRequest}.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The callbacks to be utilized for this request.  Note
      *                        the callbacks must not be shared - they uniquely specify
@@ -2515,7 +2519,8 @@
      * <p>
      * The request may be released normally by calling
      * {@link #releaseNetworkRequest(android.app.PendingIntent)}.
-     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * @param request {@link NetworkRequest} describing this request.
      * @param operation Action to perform when the network is available (corresponds
      *                  to the {@link NetworkCallback#onAvailable} call.  Typically
@@ -2556,6 +2561,8 @@
      * Registers to receive notifications about all networks which satisfy the given
      * {@link NetworkRequest}.  The callbacks will continue to be called until
      * either the application exits or {@link #unregisterNetworkCallback} is called
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index c1b4a1f..89d23a2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -104,7 +104,7 @@
 
     void setGlobalProxy(in ProxyInfo p);
 
-    ProxyInfo getDefaultProxy();
+    ProxyInfo getProxyForNetwork(in Network nework);
 
     boolean prepareVpn(String oldPackage, String newPackage, int userId);
 
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 8b0dfc9..31aedad 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -88,6 +88,54 @@
     /**
      * @hide
      */
+    public enum ProvisioningChange {
+        STILL_NOT_PROVISIONED,
+        LOST_PROVISIONING,
+        GAINED_PROVISIONING,
+        STILL_PROVISIONED,
+    }
+
+    /**
+     * Compare the provisioning states of two LinkProperties instances.
+     *
+     * @hide
+     */
+    public static ProvisioningChange compareProvisioning(
+            LinkProperties before, LinkProperties after) {
+        if (before.isProvisioned() && after.isProvisioned()) {
+            // On dualstack networks, DHCPv4 renewals can occasionally fail.
+            // When this happens, IPv6-reachable services continue to function
+            // normally but IPv4-only services (naturally) fail.
+            //
+            // When an application using an IPv4-only service reports a bad
+            // network condition to the framework, attempts to re-validate
+            // the network succeed (since we support IPv6-only networks) and
+            // nothing is changed.
+            //
+            // For users, this is confusing and unexpected behaviour, and is
+            // not necessarily easy to diagnose.  Therefore, we treat changing
+            // from a dualstack network to an IPv6-only network equivalent to
+            // a total loss of provisioning.
+            //
+            // For one such example of this, see b/18867306.
+            //
+            // TODO: Remove this special case altogether.
+            if (before.isIPv4Provisioned() && !after.isIPv4Provisioned()) {
+                return ProvisioningChange.LOST_PROVISIONING;
+            }
+            return ProvisioningChange.STILL_PROVISIONED;
+        } else if (before.isProvisioned() && !after.isProvisioned()) {
+            return ProvisioningChange.LOST_PROVISIONING;
+        } else if (!before.isProvisioned() && after.isProvisioned()) {
+            return ProvisioningChange.GAINED_PROVISIONING;
+        } else {  // !before.isProvisioned() && !after.isProvisioned()
+            return ProvisioningChange.STILL_NOT_PROVISIONED;
+        }
+    }
+
+    /**
+     * @hide
+     */
     public LinkProperties() {
     }
 
@@ -287,6 +335,20 @@
     }
 
     /**
+     * Removes the given {@link InetAddress} from the list of DNS servers.
+     *
+     * @param dnsServer The {@link InetAddress} to remove from the list of DNS servers.
+     * @return true if the DNS server was removed, false if it did not exist.
+     * @hide
+     */
+    public boolean removeDnsServer(InetAddress dnsServer) {
+        if (dnsServer != null) {
+            return mDnses.remove(dnsServer);
+        }
+        return false;
+    }
+
+    /**
      * Replaces the DNS servers in this {@code LinkProperties} with
      * the given {@link Collection} of {@link InetAddress} objects.
      *
@@ -679,8 +741,9 @@
      * This requires an IP address, default route, and DNS server.
      *
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
+     * @hide
      */
-    private boolean hasIPv4() {
+    public boolean isIPv4Provisioned() {
         return (hasIPv4Address() &&
                 hasIPv4DefaultRoute() &&
                 hasIPv4DnsServer());
@@ -691,8 +754,9 @@
      * This requires an IP address, default route, and DNS server.
      *
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
+     * @hide
      */
-    private boolean hasIPv6() {
+    public boolean isIPv6Provisioned() {
         return (hasGlobalIPv6Address() &&
                 hasIPv6DefaultRoute() &&
                 hasIPv6DnsServer());
@@ -706,7 +770,7 @@
      * @hide
      */
     public boolean isProvisioned() {
-        return (hasIPv4() || hasIPv6());
+        return (isIPv4Provisioned() || isIPv6Provisioned());
     }
 
     /**
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 67ecb5d..754c6b3 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -247,12 +247,7 @@
             throw new IOException("No ConnectivityManager yet constructed, please construct one");
         }
         // TODO: Should this be optimized to avoid fetching the global proxy for every request?
-        ProxyInfo proxyInfo = cm.getGlobalProxy();
-        if (proxyInfo == null) {
-            // TODO: Should this be optimized to avoid fetching LinkProperties for every request?
-            final LinkProperties lp = cm.getLinkProperties(this);
-            if (lp != null) proxyInfo = lp.getHttpProxy();
-        }
+        final ProxyInfo proxyInfo = cm.getProxyForNetwork(this);
         java.net.Proxy proxy = null;
         if (proxyInfo != null) {
             proxy = proxyInfo.makeProxy();
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 9c3a623..e6fc1ea 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -27,6 +27,7 @@
 import com.android.internal.util.Protocol;
 
 import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * A Utility class for handling for communicating between bearer-specific
@@ -51,6 +52,8 @@
     private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
     private volatile long mLastBwRefreshTime = 0;
     private static final long BW_REFRESH_MIN_WIN_MS = 500;
+    private boolean mPollLceScheduled = false;
+    private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
 
     private static final int BASE = Protocol.BASE_NETWORK_AGENT;
 
@@ -207,11 +210,23 @@
                 break;
             }
             case CMD_REQUEST_BANDWIDTH_UPDATE: {
+                long currentTimeMs = System.currentTimeMillis();
                 if (VDBG) {
                     log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
                 }
-                if (System.currentTimeMillis() > (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
-                    pollLceData();
+                if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+                    mPollLceScheduled = false;
+                    if (mPollLcePending.getAndSet(true) == false) {
+                        pollLceData();
+                    }
+                } else {
+                    // deliver the request at a later time rather than discard it completely.
+                    if (!mPollLceScheduled) {
+                        long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS -
+                                currentTimeMs + 1;
+                        mPollLceScheduled = sendEmptyMessageDelayed(
+                                CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
+                    }
                 }
                 break;
             }
@@ -260,6 +275,7 @@
      * Called by the bearer code when it has new NetworkCapabilities data.
      */
     public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
+        mPollLcePending.set(false);
         mLastBwRefreshTime = System.currentTimeMillis();
         queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
                 new NetworkCapabilities(networkCapabilities));
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 29dd8ad..4487cab 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -194,6 +194,12 @@
     public native static boolean protectFromVpn(int socketfd);
 
     /**
+     * Determine if {@code uid} can access network designated by {@code netId}.
+     * @return {@code true} if {@code uid} can access network, {@code false} otherwise.
+     */
+    public native static boolean queryUserAccess(int uid, int netId);
+
+    /**
      * Convert a IPv4 address from an integer to an InetAddress.
      * @param hostAddress an int corresponding to the IPv4 address in network byte order
      */
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 2c90909..5f5e623 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -35,13 +35,7 @@
  *
  * Other HTTP stacks will need to obtain the proxy info from
  * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}.
- *
- * @deprecated Please use {@link java.net.URL#openConnection}, {@link java.net.Proxy} and
- *     friends. The Apache HTTP client is no longer maintained and may be removed in a future
- *     release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
  */
-@Deprecated
 public class ProxyInfo implements Parcelable {
 
     private String mHost;
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 60e8ed0..fada7ac 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -291,6 +291,11 @@
     return (jboolean) !protectFromVpn(socket);
 }
 
+static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
+{
+    return (jboolean) !queryUserAccess(uid, netId);
+}
+
 
 // ----------------------------------------------------------------------------
 
@@ -311,6 +316,7 @@
     { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
     { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
     { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+    { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
     { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
 };
 
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index abfed6e..5c55efb 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.LinkProperties;
+import android.net.LinkProperties.ProvisioningChange;
 import android.net.RouteInfo;
 import android.system.OsConstants;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -34,7 +35,8 @@
     private static InetAddress DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888");
     private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1");
     private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1");
-    private static InetAddress GATEWAY6 = NetworkUtils.numericToInetAddress("fe80::6:0000:613");
+    private static InetAddress GATEWAY61 = NetworkUtils.numericToInetAddress("fe80::6:0000:613");
+    private static InetAddress GATEWAY62 = NetworkUtils.numericToInetAddress("fe80::6:2222");
     private static String NAME = "qmi0";
     private static int MTU = 1500;
 
@@ -466,6 +468,8 @@
         assertFalse("v4only:addr+dns", lp4.isProvisioned());
         lp4.addRoute(new RouteInfo(GATEWAY1));
         assertTrue("v4only:addr+dns+route", lp4.isProvisioned());
+        assertTrue("v4only:addr+dns+route", lp4.isIPv4Provisioned());
+        assertFalse("v4only:addr+dns+route", lp4.isIPv6Provisioned());
 
         LinkProperties lp6 = new LinkProperties();
         assertFalse("v6only:empty", lp6.isProvisioned());
@@ -473,11 +477,14 @@
         assertFalse("v6only:fe80-only", lp6.isProvisioned());
         lp6.addDnsServer(DNS6);
         assertFalse("v6only:fe80+dns", lp6.isProvisioned());
-        lp6.addRoute(new RouteInfo(GATEWAY6));
+        lp6.addRoute(new RouteInfo(GATEWAY61));
         assertFalse("v6only:fe80+dns+route", lp6.isProvisioned());
         lp6.addLinkAddress(LINKADDRV6);
+        assertTrue("v6only:fe80+global+dns+route", lp6.isIPv6Provisioned());
         assertTrue("v6only:fe80+global+dns+route", lp6.isProvisioned());
         lp6.removeLinkAddress(LINKADDRV6LINKLOCAL);
+        assertFalse("v6only:global+dns+route", lp6.isIPv4Provisioned());
+        assertTrue("v6only:global+dns+route", lp6.isIPv6Provisioned());
         assertTrue("v6only:global+dns+route", lp6.isProvisioned());
 
         LinkProperties lp46 = new LinkProperties();
@@ -487,15 +494,77 @@
         lp46.addDnsServer(DNS6);
         assertFalse("dualstack:missing-routes", lp46.isProvisioned());
         lp46.addRoute(new RouteInfo(GATEWAY1));
+        assertTrue("dualstack:v4-provisioned", lp46.isIPv4Provisioned());
+        assertFalse("dualstack:v4-provisioned", lp46.isIPv6Provisioned());
         assertTrue("dualstack:v4-provisioned", lp46.isProvisioned());
-        lp6.addRoute(new RouteInfo(GATEWAY6));
+        lp46.addRoute(new RouteInfo(GATEWAY61));
+        assertTrue("dualstack:both-provisioned", lp46.isIPv4Provisioned());
+        assertTrue("dualstack:both-provisioned", lp46.isIPv6Provisioned());
         assertTrue("dualstack:both-provisioned", lp46.isProvisioned());
 
         // A link with an IPv6 address and default route, but IPv4 DNS server.
         LinkProperties mixed = new LinkProperties();
         mixed.addLinkAddress(LINKADDRV6);
         mixed.addDnsServer(DNS1);
-        mixed.addRoute(new RouteInfo(GATEWAY6));
+        mixed.addRoute(new RouteInfo(GATEWAY61));
+        assertFalse("mixed:addr6+route6+dns4", mixed.isIPv4Provisioned());
+        assertFalse("mixed:addr6+route6+dns4", mixed.isIPv6Provisioned());
         assertFalse("mixed:addr6+route6+dns4", mixed.isProvisioned());
     }
+
+    @SmallTest
+    public void testCompareProvisioning() {
+        LinkProperties v4lp = new LinkProperties();
+        v4lp.addLinkAddress(LINKADDRV4);
+        v4lp.addRoute(new RouteInfo(GATEWAY1));
+        v4lp.addDnsServer(DNS1);
+        assertTrue(v4lp.isProvisioned());
+
+        LinkProperties v4r = new LinkProperties(v4lp);
+        v4r.removeDnsServer(DNS1);
+        assertFalse(v4r.isProvisioned());
+
+        assertEquals(ProvisioningChange.STILL_NOT_PROVISIONED,
+                LinkProperties.compareProvisioning(v4r, v4r));
+        assertEquals(ProvisioningChange.LOST_PROVISIONING,
+                LinkProperties.compareProvisioning(v4lp, v4r));
+        assertEquals(ProvisioningChange.GAINED_PROVISIONING,
+                LinkProperties.compareProvisioning(v4r, v4lp));
+        assertEquals(ProvisioningChange.STILL_PROVISIONED,
+                LinkProperties.compareProvisioning(v4lp, v4lp));
+
+        // Check that losing IPv4 provisioning on a dualstack network is
+        // seen as a total loss of provisioning.
+        LinkProperties v6lp = new LinkProperties();
+        v6lp.addLinkAddress(LINKADDRV6);
+        v6lp.addRoute(new RouteInfo(GATEWAY61));
+        v6lp.addDnsServer(DNS6);
+        assertFalse(v6lp.isIPv4Provisioned());
+        assertTrue(v6lp.isIPv6Provisioned());
+        assertTrue(v6lp.isProvisioned());
+
+        LinkProperties v46lp = new LinkProperties(v6lp);
+        v46lp.addLinkAddress(LINKADDRV4);
+        v46lp.addRoute(new RouteInfo(GATEWAY1));
+        v46lp.addDnsServer(DNS1);
+        assertTrue(v46lp.isIPv4Provisioned());
+        assertTrue(v46lp.isIPv6Provisioned());
+        assertTrue(v46lp.isProvisioned());
+
+        assertEquals(ProvisioningChange.STILL_PROVISIONED,
+                LinkProperties.compareProvisioning(v6lp, v46lp));
+        assertEquals(ProvisioningChange.LOST_PROVISIONING,
+                LinkProperties.compareProvisioning(v46lp, v6lp));
+
+        // Check that losing and gaining a secondary router does not change
+        // the provisioning status.
+        LinkProperties v6lp2 = new LinkProperties(v6lp);
+        v6lp2.addRoute(new RouteInfo(GATEWAY62));
+        assertTrue(v6lp2.isProvisioned());
+
+        assertEquals(ProvisioningChange.STILL_PROVISIONED,
+                LinkProperties.compareProvisioning(v6lp2, v6lp));
+        assertEquals(ProvisioningChange.STILL_PROVISIONED,
+                LinkProperties.compareProvisioning(v6lp, v6lp2));
+    }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4be33a4..2e4b881 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -199,7 +199,6 @@
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
 
-    private Object mDnsLock = new Object();
     private int mNumDnsEntries;
 
     private boolean mTestMode;
@@ -543,16 +542,24 @@
         }
 
         public void dump(IndentingPrintWriter pw) {
+            pw.println("mLegacyTypeTracker:");
+            pw.increaseIndent();
+            pw.print("Supported types:");
             for (int type = 0; type < mTypeLists.length; type++) {
-                if (mTypeLists[type] == null) continue;
-                pw.print(type + " ");
-                pw.increaseIndent();
-                if (mTypeLists[type].size() == 0) pw.println("none");
-                for (NetworkAgentInfo nai : mTypeLists[type]) {
-                    pw.println(naiToString(nai));
-                }
-                pw.decreaseIndent();
+                if (mTypeLists[type] != null) pw.print(" " + type);
             }
+            pw.println();
+            pw.println("Current state:");
+            pw.increaseIndent();
+            for (int type = 0; type < mTypeLists.length; type++) {
+                if (mTypeLists[type] == null|| mTypeLists[type].size() == 0) continue;
+                for (NetworkAgentInfo nai : mTypeLists[type]) {
+                    pw.println(type + " " + naiToString(nai));
+                }
+            }
+            pw.decreaseIndent();
+            pw.decreaseIndent();
+            pw.println();
         }
 
         // This class needs its own log method because it has a different TAG.
@@ -1750,12 +1757,11 @@
             return;
         }
 
-        pw.println("NetworkFactories for:");
-        pw.increaseIndent();
+        pw.print("NetworkFactories for:");
         for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
-            pw.println(nfi.name);
+            pw.print(" " + nfi.name);
         }
-        pw.decreaseIndent();
+        pw.println();
         pw.println();
 
         NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId);
@@ -1795,22 +1801,22 @@
         pw.println();
         pw.decreaseIndent();
 
-        pw.println("mLegacyTypeTracker:");
-        pw.increaseIndent();
         mLegacyTypeTracker.dump(pw);
-        pw.decreaseIndent();
-        pw.println();
 
         synchronized (this) {
-            pw.println("NetworkTransitionWakeLock is currently " +
-                    (mNetTransitionWakeLock.isHeld() ? "" : "not ") + "held.");
-            pw.println("It was last requested for "+mNetTransitionWakeLockCausedBy);
+            pw.print("mNetTransitionWakeLock: currently " +
+                    (mNetTransitionWakeLock.isHeld() ? "" : "not ") + "held");
+            if (!TextUtils.isEmpty(mNetTransitionWakeLockCausedBy)) {
+                pw.println(", last requested for " + mNetTransitionWakeLockCausedBy);
+            } else {
+                pw.println(", last requested never");
+            }
         }
         pw.println();
 
         mTethering.dump(fd, pw, args);
 
-        if (mInetLog != null) {
+        if (mInetLog != null && mInetLog.size() > 0) {
             pw.println();
             pw.println("Inet condition reports:");
             pw.increaseIndent();
@@ -2688,7 +2694,7 @@
                 actionToken);
     }
 
-    public ProxyInfo getDefaultProxy() {
+    private ProxyInfo getDefaultProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
         // of proxy info to all the JVMs.
@@ -2700,6 +2706,22 @@
         }
     }
 
+    public ProxyInfo getProxyForNetwork(Network network) {
+        if (network == null) return getDefaultProxy();
+        final ProxyInfo globalProxy = getGlobalProxy();
+        if (globalProxy != null) return globalProxy;
+        if (!NetworkUtils.queryUserAccess(Binder.getCallingUid(), network.netId)) return null;
+        // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
+        // caller may not have.
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai == null) return null;
+        synchronized (nai) {
+            final ProxyInfo proxyInfo = nai.linkProperties.getHttpProxy();
+            if (proxyInfo == null) return null;
+            return new ProxyInfo(proxyInfo);
+        }
+    }
+
     // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
     // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
     // proxy is null then there is no proxy in place).
@@ -3624,6 +3646,7 @@
 
     @Override
     public void releasePendingNetworkRequest(PendingIntent operation) {
+        checkNotNull(operation, "PendingIntent cannot be null.");
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
                 getCallingUid(), 0, operation));
     }
@@ -4588,27 +4611,36 @@
     @Override
     public void factoryReset() {
         enforceConnectivityInternalPermission();
+
+        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
+            return;
+        }
+
         final int userId = UserHandle.getCallingUserId();
 
         // Turn airplane mode off
         setAirplaneMode(false);
 
-        // Untether
-        for (String tether : getTetheredIfaces()) {
-            untether(tether);
+        if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
+            // Untether
+            for (String tether : getTetheredIfaces()) {
+                untether(tether);
+            }
         }
 
-        // Turn VPN off
-        VpnConfig vpnConfig = getVpnConfig(userId);
-        if (vpnConfig != null) {
-            if (vpnConfig.legacy) {
-                prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
-            } else {
-                // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
-                // in the future without user intervention.
-                setVpnPackageAuthorization(vpnConfig.user, userId, false);
+        if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+            // Turn VPN off
+            VpnConfig vpnConfig = getVpnConfig(userId);
+            if (vpnConfig != null) {
+                if (vpnConfig.legacy) {
+                    prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
+                } else {
+                    // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
+                    // in the future without user intervention.
+                    setVpnPackageAuthorization(vpnConfig.user, userId, false);
 
-                prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN, userId);
+                    prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN, userId);
+                }
             }
         }
     }