Merge "Stop complaining about failure to remove routes"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index de16985..ef4209f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -373,10 +373,11 @@
     }
 
     /**
-     * Gets you info about the current data network.
-     * Call {@link NetworkInfo#isConnected()} on the returned {@link NetworkInfo}
-     * to check if the device has a data connection.
-    */
+     * Returns details about the currently active data network. When connected,
+     * this network is the default route for outgoing connections. You should
+     * always check {@link NetworkInfo#isConnected()} before initiating network
+     * traffic. This may return {@code null} when no networks are available.
+     */
     public NetworkInfo getActiveNetworkInfo() {
         try {
             return mService.getActiveNetworkInfo();
@@ -856,4 +857,19 @@
         } catch (RemoteException e) {}
         return false;
     }
+
+    /**
+     * Returns if the currently active data network is metered. A network is
+     * classified as metered when the user is sensitive to heavy data usage on
+     * that connection. You should check this before doing large data transfers,
+     * and warn the user or delay the operation until another network is
+     * available.
+     */
+    public boolean isActiveNetworkMetered() {
+        try {
+            return mService.isActiveNetworkMetered();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 7046008..92aeff2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -51,6 +51,7 @@
     NetworkState[] getAllNetworkState();
 
     NetworkQuotaInfo getActiveNetworkQuotaInfo();
+    boolean isActiveNetworkMetered();
 
     boolean setRadios(boolean onOff);
 
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 724d9fb..1f2b1ae 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -31,20 +31,22 @@
 int dhcp_do_request(const char *ifname,
                     const char *ipaddr,
                     const char *gateway,
-                    uint32_t  *prefixLength,
+                    uint32_t *prefixLength,
                     const char *dns1,
                     const char *dns2,
                     const char *server,
-                    uint32_t  *lease);
+                    uint32_t *lease,
+                    const char *vendorInfo);
 
 int dhcp_do_request_renew(const char *ifname,
                     const char *ipaddr,
                     const char *gateway,
-                    uint32_t  *prefixLength,
+                    uint32_t *prefixLength,
                     const char *dns1,
                     const char *dns2,
                     const char *server,
-                    uint32_t  *lease);
+                    uint32_t *lease,
+                    const char *vendorInfo);
 
 int dhcp_stop(const char *ifname);
 int dhcp_release_lease(const char *ifname);
@@ -68,6 +70,7 @@
     jfieldID dns2;
     jfieldID serverAddress;
     jfieldID leaseDuration;
+    jfieldID vendorInfo;
 } dhcpInfoInternalFieldIds;
 
 static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname)
@@ -116,16 +119,17 @@
     char    dns2[PROPERTY_VALUE_MAX];
     char  server[PROPERTY_VALUE_MAX];
     uint32_t lease;
+    char vendorInfo[PROPERTY_VALUE_MAX];
 
     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
     if (nameStr == NULL) return (jboolean)false;
 
     if (renew) {
         result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
-                dns1, dns2, server, &lease);
+                dns1, dns2, server, &lease, vendorInfo);
     } else {
         result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
-                dns1, dns2, server, &lease);
+                dns1, dns2, server, &lease, vendorInfo);
     }
 
     env->ReleaseStringUTFChars(ifname, nameStr);
@@ -161,6 +165,7 @@
         env->SetObjectField(info, dhcpInfoInternalFieldIds.serverAddress,
                 env->NewStringUTF(server));
         env->SetIntField(info, dhcpInfoInternalFieldIds.leaseDuration, lease);
+        env->SetObjectField(info, dhcpInfoInternalFieldIds.vendorInfo, env->NewStringUTF(vendorInfo));
     }
     return (jboolean)(result == 0);
 }
@@ -230,6 +235,7 @@
     dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalClass, "dns2", "Ljava/lang/String;");
     dhcpInfoInternalFieldIds.serverAddress = env->GetFieldID(dhcpInfoInternalClass, "serverAddress", "Ljava/lang/String;");
     dhcpInfoInternalFieldIds.leaseDuration = env->GetFieldID(dhcpInfoInternalClass, "leaseDuration", "I");
+    dhcpInfoInternalFieldIds.vendorInfo = env->GetFieldID(dhcpInfoInternalClass, "vendorInfo", "Ljava/lang/String;");
 
     return AndroidRuntime::registerNativeMethods(env,
             NETUTILS_PKG_NAME, gNetworkUtilMethods, NELEM(gNetworkUtilMethods));
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index c91b268..faa8d3c 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -865,14 +865,39 @@
     @Override
     public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
         enforceAccessPermission();
-        final NetworkState state = getNetworkStateUnchecked(mActiveDefaultNetwork);
-        if (state != null) {
-            try {
-                return mPolicyManager.getNetworkQuotaInfo(state);
-            } catch (RemoteException e) {
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final NetworkState state = getNetworkStateUnchecked(mActiveDefaultNetwork);
+            if (state != null) {
+                try {
+                    return mPolicyManager.getNetworkQuotaInfo(state);
+                } catch (RemoteException e) {
+                }
             }
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
-        return null;
+    }
+
+    @Override
+    public boolean isActiveNetworkMetered() {
+        enforceAccessPermission();
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final NetworkState state = getNetworkStateUnchecked(mActiveDefaultNetwork);
+            if (state != null) {
+                try {
+                    return mPolicyManager.isNetworkMetered(state);
+                } catch (RemoteException e) {
+                }
+            }
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     public boolean setRadios(boolean turnOn) {