Merge "Listen for network disconnect." into jb-mr2-dev
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 9e9b43d..e5d6e51 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -129,4 +129,6 @@
     void captivePortalCheckComplete(in NetworkInfo info);
 
     void supplyMessenger(int networkType, in Messenger messenger);
+
+    int findConnectionTypeForIface(in String iface);
 }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 01625dd..c2f4a2c 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -544,7 +544,7 @@
                                   mTethering.getTetherableBluetoothRegexs().length != 0) &&
                                  mTethering.getUpstreamIfaceTypes().length != 0);
 
-        mVpn = new Vpn(mContext, mVpnCallback, mNetd);
+        mVpn = new Vpn(mContext, mVpnCallback, mNetd, this);
         mVpn.startMonitoring(mContext, mTrackerHandler);
 
         mClat = new Nat464Xlat(mContext, mNetd, this, mTrackerHandler);
@@ -3448,4 +3448,19 @@
             mNetTrackers[networkType].supplyMessenger(messenger);
         }
     }
+
+    public int findConnectionTypeForIface(String iface) {
+        enforceConnectivityInternalPermission();
+
+        if (TextUtils.isEmpty(iface)) return ConnectivityManager.TYPE_NONE;
+        for (NetworkStateTracker tracker : mNetTrackers) {
+            if (tracker != null) {
+                LinkProperties lp = tracker.getLinkProperties();
+                if (lp != null && iface.equals(lp.getInterfaceName())) {
+                    return tracker.getNetworkInfo().getType();
+                }
+            }
+        }
+        return ConnectivityManager.TYPE_NONE;
+    }
 }
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 533db46..10c7e27 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -21,9 +21,11 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -33,6 +35,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.BaseNetworkStateTracker;
 import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.LinkProperties;
 import android.net.LocalSocket;
@@ -71,6 +74,7 @@
 import java.net.InetAddress;
 import java.nio.charset.Charsets;
 import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import libcore.io.IoUtils;
 
@@ -92,12 +96,15 @@
     private LegacyVpnRunner mLegacyVpnRunner;
     private PendingIntent mStatusIntent;
     private boolean mEnableNotif = true;
+    private final IConnectivityManager mConnService;
 
-    public Vpn(Context context, VpnCallback callback, INetworkManagementService netService) {
+    public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
+            IConnectivityManager connService) {
         // TODO: create dedicated TYPE_VPN network type
         super(ConnectivityManager.TYPE_DUMMY);
         mContext = context;
         mCallback = callback;
+        mConnService = connService;
 
         try {
             netService.registerObserver(mObserver);
@@ -562,7 +569,6 @@
         if (!profile.searchDomains.isEmpty()) {
             config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
         }
-
         startLegacyVpn(config, racoon, mtpd);
     }
 
@@ -630,9 +636,32 @@
         private final String[][] mArguments;
         private final LocalSocket[] mSockets;
         private final String mOuterInterface;
+        private final AtomicInteger mOuterConnection =
+                new AtomicInteger(ConnectivityManager.TYPE_NONE);
 
         private long mTimer = -1;
 
+        /**
+         * Watch for the outer connection (passing in the constructor) going away.
+         */
+        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                    if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
+                            ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
+                        NetworkInfo info = (NetworkInfo)intent.getExtra(
+                                ConnectivityManager.EXTRA_NETWORK_INFO);
+                        if (info != null && !info.isConnectedOrConnecting()) {
+                            try {
+                                mObserver.interfaceStatusChanged(mOuterInterface, false);
+                            } catch (RemoteException e) {}
+                        }
+                    }
+                }
+            }
+        };
+
         public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
             super(TAG);
             mConfig = config;
@@ -644,7 +673,22 @@
             // This is the interface which VPN is running on,
             // mConfig.interfaze will change to point to OUR
             // internal interface soon. TODO - add inner/outer to mconfig
+            // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
+            // we will leave the VPN up.  We should check that it's still there/connected after 
+            // registering
             mOuterInterface = mConfig.interfaze;
+
+            try {
+                mOuterConnection.set(
+                        mConnService.findConnectionTypeForIface(mOuterInterface));
+            } catch (Exception e) {
+                mOuterConnection.set(ConnectivityManager.TYPE_NONE);
+            }
+
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+            mContext.registerReceiver(mBroadcastReceiver, filter);
+
         }
 
         public void check(String interfaze) {
@@ -661,6 +705,9 @@
                 IoUtils.closeQuietly(socket);
             }
             updateState(DetailedState.DISCONNECTED, "exit");
+            try {
+                mContext.unregisterReceiver(mBroadcastReceiver);
+            } catch (IllegalArgumentException e) {}
         }
 
         @Override