am 685a0a72: am bbd87eb9: Merge "Track libcore.os\' move to android.system."

* commit '685a0a72d445515167a2071330679cdf9b53a62d':
  Track libcore.os' move to android.system.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c78a973..4eecfa9 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -361,11 +361,17 @@
      */
     public static final int TYPE_MOBILE_IA = 14;
 
-    /** {@hide} */
-    public static final int MAX_RADIO_TYPE   = TYPE_MOBILE_IA;
+    /**
+     * The network that uses proxy to achieve connectivity.
+     * {@hide}
+     */
+    public static final int TYPE_PROXY = 16;
 
     /** {@hide} */
-    public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_IA;
+    public static final int MAX_RADIO_TYPE   = TYPE_PROXY;
+
+    /** {@hide} */
+    public static final int MAX_NETWORK_TYPE = TYPE_PROXY;
 
     /**
      * If you want to set the default network preference,you can directly
@@ -395,6 +401,8 @@
 
     private final IConnectivityManager mService;
 
+    private final String mPackageName;
+
     /**
      * Tests if a given integer represents a valid network type.
      * @param networkType the type to be tested
@@ -444,6 +452,8 @@
                 return "WIFI_P2P";
             case TYPE_MOBILE_IA:
                 return "MOBILE_IA";
+            case TYPE_PROXY:
+                return "PROXY";
             default:
                 return Integer.toString(type);
         }
@@ -802,7 +812,7 @@
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
         byte[] address = hostAddress.getAddress();
         try {
-            return mService.requestRouteToHostAddress(networkType, address);
+            return mService.requestRouteToHostAddress(networkType, address, mPackageName);
         } catch (RemoteException e) {
             return false;
         }
@@ -898,8 +908,9 @@
     /**
      * {@hide}
      */
-    public ConnectivityManager(IConnectivityManager service) {
+    public ConnectivityManager(IConnectivityManager service, String packageName) {
         mService = checkNotNull(service, "missing IConnectivityManager");
+        mPackageName = checkNotNull(packageName, "missing package name");
     }
 
     /** {@hide} */
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index c1da2e3..4bca7fe 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -71,9 +71,9 @@
 
     int stopUsingNetworkFeature(int networkType, in String feature);
 
-    boolean requestRouteToHost(int networkType, int hostAddress);
+    boolean requestRouteToHost(int networkType, int hostAddress, String packageName);
 
-    boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
+    boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, String packageName);
 
     boolean getMobileDataEnabled();
     void setMobileDataEnabled(boolean enabled);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
similarity index 96%
rename from services/java/com/android/server/ConnectivityService.java
rename to services/core/java/com/android/server/ConnectivityService.java
index d42ae3a..57ee031 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -26,12 +26,14 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
 import android.app.AlarmManager;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -43,7 +45,9 @@
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -69,6 +73,7 @@
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.net.Proxy;
+import android.net.ProxyDataTracker;
 import android.net.ProxyProperties;
 import android.net.RouteInfo;
 import android.net.SamplingDataTracker;
@@ -412,6 +417,8 @@
 
     private SettingsObserver mSettingsObserver;
 
+    private AppOpsManager mAppOpsManager;
+
     NetworkConfig[] mNetConfigs;
     int mNetworksDefined;
 
@@ -695,6 +702,8 @@
         filter = new IntentFilter();
         filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
         mContext.registerReceiver(mProvisioningReceiver, filter);
+
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
     }
 
     /**
@@ -729,6 +738,8 @@
                     return makeWimaxStateTracker(mContext, mTrackerHandler);
                 case TYPE_ETHERNET:
                     return EthernetDataTracker.getInstance();
+                case TYPE_PROXY:
+                    return new ProxyDataTracker();
                 default:
                     throw new IllegalArgumentException(
                             "Trying to create a NetworkStateTracker for an unknown radio type: "
@@ -1527,6 +1538,40 @@
     }
 
     /**
+     * Check if the address falls into any of currently running VPN's route's.
+     */
+    private boolean isAddressUnderVpn(InetAddress address) {
+        synchronized (mVpns) {
+            synchronized (mRoutesLock) {
+                int uid = UserHandle.getCallingUserId();
+                Vpn vpn = mVpns.get(uid);
+                if (vpn == null) {
+                    return false;
+                }
+
+                // Check if an exemption exists for this address.
+                for (LinkAddress destination : mExemptAddresses) {
+                    if (!NetworkUtils.addressTypeMatches(address, destination.getAddress())) {
+                        continue;
+                    }
+
+                    int prefix = destination.getNetworkPrefixLength();
+                    InetAddress addrMasked = NetworkUtils.getNetworkPart(address, prefix);
+                    InetAddress destMasked = NetworkUtils.getNetworkPart(destination.getAddress(),
+                            prefix);
+
+                    if (addrMasked.equals(destMasked)) {
+                        return false;
+                    }
+                }
+
+                // Finally check if the address is covered by the VPN.
+                return vpn.isAddressCovered(address);
+            }
+        }
+    }
+
+    /**
      * @deprecated use requestRouteToHostAddress instead
      *
      * Ensure that a network route exists to deliver traffic to the specified
@@ -1537,14 +1582,14 @@
      * desired
      * @return {@code true} on success, {@code false} on failure
      */
-    public boolean requestRouteToHost(int networkType, int hostAddress) {
+    public boolean requestRouteToHost(int networkType, int hostAddress, String packageName) {
         InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
 
         if (inetAddress == null) {
             return false;
         }
 
-        return requestRouteToHostAddress(networkType, inetAddress.getAddress());
+        return requestRouteToHostAddress(networkType, inetAddress.getAddress(), packageName);
     }
 
     /**
@@ -1556,11 +1601,40 @@
      * desired
      * @return {@code true} on success, {@code false} on failure
      */
-    public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+    public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress,
+            String packageName) {
         enforceChangePermission();
         if (mProtectedNetworks.contains(networkType)) {
             enforceConnectivityInternalPermission();
         }
+        boolean exempt;
+        InetAddress addr;
+        try {
+            addr = InetAddress.getByAddress(hostAddress);
+        } catch (UnknownHostException e) {
+            if (DBG) log("requestRouteToHostAddress got " + e.toString());
+            return false;
+        }
+        // System apps may request routes bypassing the VPN to keep other networks working.
+        if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+            exempt = true;
+        } else {
+            mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
+            try {
+                ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(packageName,
+                        0);
+                exempt = (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+            } catch (NameNotFoundException e) {
+                throw new IllegalArgumentException("Failed to find calling package details", e);
+            }
+        }
+
+        // Non-exempt routeToHost's can only be added if the host is not covered by the VPN.
+        // This can be either because the VPN's routes do not cover the destination or a
+        // system application added an exemption that covers this destination.
+        if (!exempt && isAddressUnderVpn(addr)) {
+            return false;
+        }
 
         if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
             if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
@@ -1587,18 +1661,13 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
-            InetAddress addr = InetAddress.getByAddress(hostAddress);
             LinkProperties lp = tracker.getLinkProperties();
-            boolean ok = addRouteToAddress(lp, addr, EXEMPT);
+            boolean ok = addRouteToAddress(lp, addr, exempt);
             if (DBG) log("requestRouteToHostAddress ok=" + ok);
             return ok;
-        } catch (UnknownHostException e) {
-            if (DBG) log("requestRouteToHostAddress got " + e.toString());
         } finally {
             Binder.restoreCallingIdentity(token);
         }
-        if (DBG) log("requestRouteToHostAddress X bottom return false");
-        return false;
     }
 
     private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable,
@@ -2308,9 +2377,9 @@
             mInetConditionChangeInFlight = false;
             // Don't do this - if we never sign in stay, grey
             //reportNetworkCondition(mActiveDefaultNetwork, 100);
+            updateNetworkSettings(thisNet);
         }
         thisNet.setTeardownRequested(false);
-        updateNetworkSettings(thisNet);
         updateMtuSizeSettings(thisNet);
         handleConnectivityChange(newNetType, false);
         sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
@@ -2695,6 +2764,15 @@
             }
             setBufferSize(bufferSizes);
         }
+
+        final String defaultRwndKey = "net.tcp.default_init_rwnd";
+        int defaultRwndValue = SystemProperties.getInt(defaultRwndKey, 0);
+        Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
+            Settings.Global.TCP_DEFAULT_INIT_RWND, defaultRwndValue);
+        final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd";
+        if (rwndValue != 0) {
+            SystemProperties.set(sysctlKey, rwndValue.toString());
+        }
     }
 
     /**
@@ -3037,7 +3115,7 @@
                 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
                     info = (NetworkInfo) msg.obj;
                     int type = info.getType();
-                    updateNetworkSettings(mNetTrackers[type]);
+                    if (mNetConfigs[type].isDefault()) updateNetworkSettings(mNetTrackers[type]);
                     break;
                 }
             }
@@ -3596,8 +3674,7 @@
             int user = UserHandle.getUserId(Binder.getCallingUid());
             if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) {
                 synchronized(mVpns) {
-                    mVpns.get(user).protect(socket,
-                            mNetTrackers[type].getLinkProperties().getInterfaceName());
+                    mVpns.get(user).protect(socket);
                 }
                 return true;
             }
@@ -3837,7 +3914,7 @@
                 boolean forwardDns) {
             try {
                 mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd);
-                if (forwardDns) mNetd.clearDnsInterfaceForUidRange(uidStart, uidEnd);
+                if (forwardDns) mNetd.clearDnsInterfaceForUidRange(interfaze, uidStart, uidEnd);
             } catch (RemoteException e) {
             }
 
@@ -3992,6 +4069,14 @@
      */
     private static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5;
 
+    /**
+     * The mobile network is provisioning
+     */
+    private static final int CMP_RESULT_CODE_IS_PROVISIONING = 6;
+
+    private AtomicBoolean mIsProvisioningNetwork = new AtomicBoolean(false);
+    private AtomicBoolean mIsStartingProvisioning = new AtomicBoolean(false);
+
     private AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false);
 
     @Override
@@ -4062,11 +4147,25 @@
                                 setProvNotificationVisible(true,
                                         ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(),
                                         url);
+                                // Mark that we've got a provisioning network and
+                                // Disable Mobile Data until user actually starts provisioning.
+                                mIsProvisioningNetwork.set(true);
+                                MobileDataStateTracker mdst = (MobileDataStateTracker)
+                                        mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+                                mdst.setInternalDataEnable(false);
                             } else {
                                 if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
                             }
                             break;
                         }
+                        case CMP_RESULT_CODE_IS_PROVISIONING: {
+                            // FIXME: Need to know when provisioning is done. Probably we can
+                            // check the completion status if successful we're done if we
+                            // "timedout" or still connected to provisioning APN turn off data?
+                            if (DBG) log("CheckMp.onComplete: provisioning started");
+                            mIsStartingProvisioning.set(false);
+                            break;
+                        }
                         default: {
                             loge("CheckMp.onComplete: ignore unexpected result=" + result);
                             break;
@@ -4216,6 +4315,12 @@
                 return result;
             }
 
+            if (mCs.mIsStartingProvisioning.get()) {
+                result = CMP_RESULT_CODE_IS_PROVISIONING;
+                log("isMobileOk: X is provisioning result=" + result);
+                return result;
+            }
+
             // See if we've already determined we've got a provisioning connection,
             // if so we don't need to do anything active.
             MobileDataStateTracker mdstDefault = (MobileDataStateTracker)
@@ -4354,7 +4459,7 @@
 
                             // Make a route to host so we check the specific interface.
                             if (mCs.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_HIPRI,
-                                    hostAddr.getAddress())) {
+                                    hostAddr.getAddress(), null)) {
                                 // Wait a short time to be sure the route is established ??
                                 log("isMobileOk:"
                                         + " wait to establish route to hostAddr=" + hostAddr);
@@ -4550,19 +4655,20 @@
     };
 
     private void handleMobileProvisioningAction(String url) {
-        // Notication mark notification as not visible
+        // Mark notification as not visible
         setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null);
 
         // If provisioning network handle as a special case,
         // otherwise launch browser with the intent directly.
-        NetworkInfo ni = getProvisioningNetworkInfo();
-        if ((ni != null) && ni.isConnectedToProvisioningNetwork()) {
-            if (DBG) log("handleMobileProvisioningAction: on provisioning network");
+        if (mIsProvisioningNetwork.get()) {
+            if (DBG) log("handleMobileProvisioningAction: on prov network enable then launch");
+            mIsStartingProvisioning.set(true);
             MobileDataStateTracker mdst = (MobileDataStateTracker)
                     mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+            mdst.setEnableFailFastMobileData(DctConstants.ENABLED);
             mdst.enableMobileProvisioning(url);
         } else {
-            if (DBG) log("handleMobileProvisioningAction: on default network");
+            if (DBG) log("handleMobileProvisioningAction: not prov network, launch browser directly");
             Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
                     Intent.CATEGORY_APP_BROWSER);
             newIntent.setData(Uri.parse(url));
diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
similarity index 100%
rename from services/java/com/android/server/connectivity/Nat464Xlat.java
rename to services/core/java/com/android/server/connectivity/Nat464Xlat.java
diff --git a/tests/CoreTests/android/core/NsdServiceInfoTest.java b/tests/CoreTests/android/core/NsdServiceInfoTest.java
new file mode 100644
index 0000000..5bf0167
--- /dev/null
+++ b/tests/CoreTests/android/core/NsdServiceInfoTest.java
@@ -0,0 +1,163 @@
+package android.core;
+
+import android.test.AndroidTestCase;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.StrictMode;
+import android.net.nsd.NsdServiceInfo;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+
+public class NsdServiceInfoTest extends AndroidTestCase {
+
+    public final static InetAddress LOCALHOST;
+    static {
+        // Because test.
+        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
+        StrictMode.setThreadPolicy(policy);
+
+        InetAddress _host = null;
+        try {
+            _host = InetAddress.getLocalHost();
+        } catch (UnknownHostException e) { }
+        LOCALHOST = _host;
+    }
+
+    public void testLimits() throws Exception {
+        NsdServiceInfo info = new NsdServiceInfo();
+
+        // Non-ASCII keys.
+        boolean exceptionThrown = false;
+        try {
+            info.setAttribute("猫", "meow");
+        } catch (IllegalArgumentException e) {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+        assertEmptyServiceInfo(info);
+
+        // ASCII keys with '=' character.
+        exceptionThrown = false;
+        try {
+            info.setAttribute("kitten=", "meow");
+        } catch (IllegalArgumentException e) {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+        assertEmptyServiceInfo(info);
+
+        // Single key + value length too long.
+        exceptionThrown = false;
+        try {
+            String longValue = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
+                    "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
+                    "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
+                    "ooooooooooooooooooooooooooooong";  // 248 characters.
+            info.setAttribute("longcat", longValue);  // Key + value == 255 characters.
+        } catch (IllegalArgumentException e) {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+        assertEmptyServiceInfo(info);
+
+        // Total TXT record length too long.
+        exceptionThrown = false;
+        int recordsAdded = 0;
+        try {
+            for (int i = 100; i < 300; ++i) {
+                // 6 char key + 5 char value + 2 bytes overhead = 13 byte record length.
+                String key = String.format("key%d", i);
+                info.setAttribute(key, "12345");
+                recordsAdded++;
+            }
+        } catch (IllegalArgumentException e) {
+            exceptionThrown = true;
+        }
+        assertTrue(exceptionThrown);
+        assertTrue(100 == recordsAdded);
+        assertTrue(info.getTxtRecord().length == 1300);
+    }
+
+    public void testParcel() throws Exception {
+        NsdServiceInfo emptyInfo = new NsdServiceInfo();
+        checkParcelable(emptyInfo);
+
+        NsdServiceInfo fullInfo = new NsdServiceInfo();
+        fullInfo.setServiceName("kitten");
+        fullInfo.setServiceType("_kitten._tcp");
+        fullInfo.setPort(4242);
+        fullInfo.setHost(LOCALHOST);
+        checkParcelable(fullInfo);
+
+        NsdServiceInfo noHostInfo = new NsdServiceInfo();
+        noHostInfo.setServiceName("kitten");
+        noHostInfo.setServiceType("_kitten._tcp");
+        noHostInfo.setPort(4242);
+        checkParcelable(noHostInfo);
+
+        NsdServiceInfo attributedInfo = new NsdServiceInfo();
+        attributedInfo.setServiceName("kitten");
+        attributedInfo.setServiceType("_kitten._tcp");
+        attributedInfo.setPort(4242);
+        attributedInfo.setHost(LOCALHOST);
+        attributedInfo.setAttribute("color", "pink");
+        attributedInfo.setAttribute("sound", (new String("にゃあ")).getBytes("UTF-8"));
+        attributedInfo.setAttribute("adorable", (String) null);
+        attributedInfo.setAttribute("sticky", "yes");
+        attributedInfo.setAttribute("siblings", new byte[] {});
+        attributedInfo.setAttribute("edge cases", new byte[] {0, -1, 127, -128});
+        attributedInfo.removeAttribute("sticky");
+        checkParcelable(attributedInfo);
+
+        // Sanity check that we actually wrote attributes to attributedInfo.
+        assertTrue(attributedInfo.getAttributes().keySet().contains("adorable"));
+        String sound = new String(attributedInfo.getAttributes().get("sound"), "UTF-8");
+        assertTrue(sound.equals("にゃあ"));
+        byte[] edgeCases = attributedInfo.getAttributes().get("edge cases");
+        assertTrue(Arrays.equals(edgeCases, new byte[] {0, -1, 127, -128}));
+        assertFalse(attributedInfo.getAttributes().keySet().contains("sticky"));
+    }
+
+    public void checkParcelable(NsdServiceInfo original) {
+        // Write to parcel.
+        Parcel p = Parcel.obtain();
+        Bundle writer = new Bundle();
+        writer.putParcelable("test_info", original);
+        writer.writeToParcel(p, 0);
+
+        // Extract from parcel.
+        p.setDataPosition(0);
+        Bundle reader = p.readBundle();
+        reader.setClassLoader(NsdServiceInfo.class.getClassLoader());
+        NsdServiceInfo result = reader.getParcelable("test_info");
+
+        // Assert equality of base fields.
+        assertEquality(original.getServiceName(), result.getServiceName());
+        assertEquality(original.getServiceType(), result.getServiceType());
+        assertEquality(original.getHost(), result.getHost());
+        assertTrue(original.getPort() == result.getPort());
+
+        // Assert equality of attribute map.
+        Map<String, byte[]> originalMap = original.getAttributes();
+        Map<String, byte[]> resultMap = result.getAttributes();
+        assertEquality(originalMap.keySet(), resultMap.keySet());
+        for (String key : originalMap.keySet()) {
+            assertTrue(Arrays.equals(originalMap.get(key), resultMap.get(key)));
+        }
+    }
+
+    public void assertEquality(Object expected, Object result) {
+        assertTrue(expected == result || expected.equals(result));
+    }
+
+    public void assertEmptyServiceInfo(NsdServiceInfo shouldBeEmpty) {
+        assertTrue(null == shouldBeEmpty.getTxtRecord());
+    }
+}