Home | History | Annotate | Download | only in connectivity
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.connectivity;
     18 
     19 import static android.Manifest.permission.BIND_VPN_SERVICE;
     20 
     21 import android.app.Notification;
     22 import android.app.NotificationManager;
     23 import android.app.PendingIntent;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.ServiceConnection;
     30 import android.content.pm.ApplicationInfo;
     31 import android.content.pm.PackageManager;
     32 import android.content.pm.ResolveInfo;
     33 import android.graphics.Bitmap;
     34 import android.graphics.Canvas;
     35 import android.graphics.drawable.Drawable;
     36 import android.net.BaseNetworkStateTracker;
     37 import android.net.ConnectivityManager;
     38 import android.net.IConnectivityManager;
     39 import android.net.INetworkManagementEventObserver;
     40 import android.net.LinkProperties;
     41 import android.net.LocalSocket;
     42 import android.net.LocalSocketAddress;
     43 import android.net.NetworkInfo;
     44 import android.net.RouteInfo;
     45 import android.net.NetworkInfo.DetailedState;
     46 import android.os.Binder;
     47 import android.os.FileUtils;
     48 import android.os.IBinder;
     49 import android.os.INetworkManagementService;
     50 import android.os.Parcel;
     51 import android.os.ParcelFileDescriptor;
     52 import android.os.Process;
     53 import android.os.RemoteException;
     54 import android.os.SystemClock;
     55 import android.os.SystemService;
     56 import android.os.UserHandle;
     57 import android.security.Credentials;
     58 import android.security.KeyStore;
     59 import android.util.Log;
     60 import android.widget.Toast;
     61 
     62 import com.android.internal.R;
     63 import com.android.internal.net.LegacyVpnInfo;
     64 import com.android.internal.net.VpnConfig;
     65 import com.android.internal.net.VpnProfile;
     66 import com.android.internal.util.Preconditions;
     67 import com.android.server.ConnectivityService.VpnCallback;
     68 import com.android.server.net.BaseNetworkObserver;
     69 
     70 import java.io.File;
     71 import java.io.InputStream;
     72 import java.io.OutputStream;
     73 import java.net.Inet4Address;
     74 import java.net.InetAddress;
     75 import java.nio.charset.Charsets;
     76 import java.util.Arrays;
     77 import java.util.concurrent.atomic.AtomicInteger;
     78 
     79 import libcore.io.IoUtils;
     80 
     81 /**
     82  * @hide
     83  */
     84 public class Vpn extends BaseNetworkStateTracker {
     85     private static final String TAG = "Vpn";
     86     private static final boolean LOGD = true;
     87 
     88     // TODO: create separate trackers for each unique VPN to support
     89     // automated reconnection
     90 
     91     private final VpnCallback mCallback;
     92 
     93     private String mPackage = VpnConfig.LEGACY_VPN;
     94     private String mInterface;
     95     private Connection mConnection;
     96     private LegacyVpnRunner mLegacyVpnRunner;
     97     private PendingIntent mStatusIntent;
     98     private volatile boolean mEnableNotif = true;
     99     private volatile boolean mEnableTeardown = true;
    100     private final IConnectivityManager mConnService;
    101 
    102     public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
    103             IConnectivityManager connService) {
    104         // TODO: create dedicated TYPE_VPN network type
    105         super(ConnectivityManager.TYPE_DUMMY);
    106         mContext = context;
    107         mCallback = callback;
    108         mConnService = connService;
    109 
    110         try {
    111             netService.registerObserver(mObserver);
    112         } catch (RemoteException e) {
    113             Log.wtf(TAG, "Problem registering observer", e);
    114         }
    115     }
    116 
    117     /**
    118      * Set if this object is responsible for showing its own notifications. When
    119      * {@code false}, notifications are handled externally by someone else.
    120      */
    121     public void setEnableNotifications(boolean enableNotif) {
    122         mEnableNotif = enableNotif;
    123     }
    124 
    125     /**
    126      * Set if this object is responsible for watching for {@link NetworkInfo}
    127      * teardown. When {@code false}, teardown is handled externally by someone
    128      * else.
    129      */
    130     public void setEnableTeardown(boolean enableTeardown) {
    131         mEnableTeardown = enableTeardown;
    132     }
    133 
    134     @Override
    135     protected void startMonitoringInternal() {
    136         // Ignored; events are sent through callbacks for now
    137     }
    138 
    139     @Override
    140     public boolean teardown() {
    141         // TODO: finish migration to unique tracker for each VPN
    142         throw new UnsupportedOperationException();
    143     }
    144 
    145     @Override
    146     public boolean reconnect() {
    147         // TODO: finish migration to unique tracker for each VPN
    148         throw new UnsupportedOperationException();
    149     }
    150 
    151     @Override
    152     public String getTcpBufferSizesPropName() {
    153         return PROP_TCP_BUFFER_UNKNOWN;
    154     }
    155 
    156     /**
    157      * Update current state, dispaching event to listeners.
    158      */
    159     private void updateState(DetailedState detailedState, String reason) {
    160         if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
    161         mNetworkInfo.setDetailedState(detailedState, reason, null);
    162         mCallback.onStateChanged(new NetworkInfo(mNetworkInfo));
    163     }
    164 
    165     /**
    166      * Prepare for a VPN application. This method is designed to solve
    167      * race conditions. It first compares the current prepared package
    168      * with {@code oldPackage}. If they are the same, the prepared
    169      * package is revoked and replaced with {@code newPackage}. If
    170      * {@code oldPackage} is {@code null}, the comparison is omitted.
    171      * If {@code newPackage} is the same package or {@code null}, the
    172      * revocation is omitted. This method returns {@code true} if the
    173      * operation is succeeded.
    174      *
    175      * Legacy VPN is handled specially since it is not a real package.
    176      * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
    177      * it can be revoked by itself.
    178      *
    179      * @param oldPackage The package name of the old VPN application.
    180      * @param newPackage The package name of the new VPN application.
    181      * @return true if the operation is succeeded.
    182      */
    183     public synchronized boolean prepare(String oldPackage, String newPackage) {
    184         // Return false if the package does not match.
    185         if (oldPackage != null && !oldPackage.equals(mPackage)) {
    186             return false;
    187         }
    188 
    189         // Return true if we do not need to revoke.
    190         if (newPackage == null ||
    191                 (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
    192             return true;
    193         }
    194 
    195         // Check if the caller is authorized.
    196         enforceControlPermission();
    197 
    198         // Reset the interface and hide the notification.
    199         if (mInterface != null) {
    200             jniReset(mInterface);
    201             final long token = Binder.clearCallingIdentity();
    202             try {
    203                 mCallback.restore();
    204                 hideNotification();
    205             } finally {
    206                 Binder.restoreCallingIdentity(token);
    207             }
    208             mInterface = null;
    209         }
    210 
    211         // Revoke the connection or stop LegacyVpnRunner.
    212         if (mConnection != null) {
    213             try {
    214                 mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
    215                         Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
    216             } catch (Exception e) {
    217                 // ignore
    218             }
    219             mContext.unbindService(mConnection);
    220             mConnection = null;
    221         } else if (mLegacyVpnRunner != null) {
    222             mLegacyVpnRunner.exit();
    223             mLegacyVpnRunner = null;
    224         }
    225 
    226         Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
    227         mPackage = newPackage;
    228         updateState(DetailedState.IDLE, "prepare");
    229         return true;
    230     }
    231 
    232     /**
    233      * Protect a socket from routing changes by binding it to the given
    234      * interface. The socket is NOT closed by this method.
    235      *
    236      * @param socket The socket to be bound.
    237      * @param interfaze The name of the interface.
    238      */
    239     public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
    240         PackageManager pm = mContext.getPackageManager();
    241         ApplicationInfo app = pm.getApplicationInfo(mPackage, 0);
    242         if (Binder.getCallingUid() != app.uid) {
    243             throw new SecurityException("Unauthorized Caller");
    244         }
    245         jniProtect(socket.getFd(), interfaze);
    246     }
    247 
    248     /**
    249      * Establish a VPN network and return the file descriptor of the VPN
    250      * interface. This methods returns {@code null} if the application is
    251      * revoked or not prepared.
    252      *
    253      * @param config The parameters to configure the network.
    254      * @return The file descriptor of the VPN interface.
    255      */
    256     public synchronized ParcelFileDescriptor establish(VpnConfig config) {
    257         // Check if the caller is already prepared.
    258         PackageManager pm = mContext.getPackageManager();
    259         ApplicationInfo app = null;
    260         try {
    261             app = pm.getApplicationInfo(mPackage, 0);
    262         } catch (Exception e) {
    263             return null;
    264         }
    265         if (Binder.getCallingUid() != app.uid) {
    266             return null;
    267         }
    268 
    269         // Check if the service is properly declared.
    270         Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
    271         intent.setClassName(mPackage, config.user);
    272         ResolveInfo info = pm.resolveService(intent, 0);
    273         if (info == null) {
    274             throw new SecurityException("Cannot find " + config.user);
    275         }
    276         if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
    277             throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
    278         }
    279 
    280         // Load the label.
    281         String label = app.loadLabel(pm).toString();
    282 
    283         // Load the icon and convert it into a bitmap.
    284         Drawable icon = app.loadIcon(pm);
    285         Bitmap bitmap = null;
    286         if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
    287             int width = mContext.getResources().getDimensionPixelSize(
    288                     android.R.dimen.notification_large_icon_width);
    289             int height = mContext.getResources().getDimensionPixelSize(
    290                     android.R.dimen.notification_large_icon_height);
    291             icon.setBounds(0, 0, width, height);
    292             bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    293             Canvas c = new Canvas(bitmap);
    294             icon.draw(c);
    295             c.setBitmap(null);
    296         }
    297 
    298         // Configure the interface. Abort if any of these steps fails.
    299         ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
    300         try {
    301             updateState(DetailedState.CONNECTING, "establish");
    302             String interfaze = jniGetName(tun.getFd());
    303             if (jniSetAddresses(interfaze, config.addresses) < 1) {
    304                 throw new IllegalArgumentException("At least one address must be specified");
    305             }
    306             if (config.routes != null) {
    307                 jniSetRoutes(interfaze, config.routes);
    308             }
    309             Connection connection = new Connection();
    310             if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
    311                 throw new IllegalStateException("Cannot bind " + config.user);
    312             }
    313             if (mConnection != null) {
    314                 mContext.unbindService(mConnection);
    315             }
    316             if (mInterface != null && !mInterface.equals(interfaze)) {
    317                 jniReset(mInterface);
    318             }
    319             mConnection = connection;
    320             mInterface = interfaze;
    321         } catch (RuntimeException e) {
    322             updateState(DetailedState.FAILED, "establish");
    323             IoUtils.closeQuietly(tun);
    324             throw e;
    325         }
    326         Log.i(TAG, "Established by " + config.user + " on " + mInterface);
    327 
    328         // Fill more values.
    329         config.user = mPackage;
    330         config.interfaze = mInterface;
    331 
    332         // Override DNS servers and show the notification.
    333         final long token = Binder.clearCallingIdentity();
    334         try {
    335             mCallback.override(config.dnsServers, config.searchDomains);
    336             showNotification(config, label, bitmap);
    337         } finally {
    338             Binder.restoreCallingIdentity(token);
    339         }
    340         // TODO: ensure that contract class eventually marks as connected
    341         updateState(DetailedState.AUTHENTICATING, "establish");
    342         return tun;
    343     }
    344 
    345     @Deprecated
    346     public synchronized void interfaceStatusChanged(String iface, boolean up) {
    347         try {
    348             mObserver.interfaceStatusChanged(iface, up);
    349         } catch (RemoteException e) {
    350             // ignored; target is local
    351         }
    352     }
    353 
    354     private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
    355         @Override
    356         public void interfaceStatusChanged(String interfaze, boolean up) {
    357             synchronized (Vpn.this) {
    358                 if (!up && mLegacyVpnRunner != null) {
    359                     mLegacyVpnRunner.check(interfaze);
    360                 }
    361             }
    362         }
    363 
    364         @Override
    365         public void interfaceRemoved(String interfaze) {
    366             synchronized (Vpn.this) {
    367                 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
    368                     final long token = Binder.clearCallingIdentity();
    369                     try {
    370                         mCallback.restore();
    371                         hideNotification();
    372                     } finally {
    373                         Binder.restoreCallingIdentity(token);
    374                     }
    375                     mInterface = null;
    376                     if (mConnection != null) {
    377                         mContext.unbindService(mConnection);
    378                         mConnection = null;
    379                         updateState(DetailedState.DISCONNECTED, "interfaceRemoved");
    380                     } else if (mLegacyVpnRunner != null) {
    381                         mLegacyVpnRunner.exit();
    382                         mLegacyVpnRunner = null;
    383                     }
    384                 }
    385             }
    386         }
    387     };
    388 
    389     private void enforceControlPermission() {
    390         // System user is allowed to control VPN.
    391         if (Binder.getCallingUid() == Process.SYSTEM_UID) {
    392             return;
    393         }
    394 
    395         try {
    396             // System dialogs are also allowed to control VPN.
    397             PackageManager pm = mContext.getPackageManager();
    398             ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
    399             if (Binder.getCallingUid() == app.uid) {
    400                 return;
    401             }
    402         } catch (Exception e) {
    403             // ignore
    404         }
    405 
    406         throw new SecurityException("Unauthorized Caller");
    407     }
    408 
    409     private class Connection implements ServiceConnection {
    410         private IBinder mService;
    411 
    412         @Override
    413         public void onServiceConnected(ComponentName name, IBinder service) {
    414             mService = service;
    415         }
    416 
    417         @Override
    418         public void onServiceDisconnected(ComponentName name) {
    419             mService = null;
    420         }
    421     }
    422 
    423     private void showNotification(VpnConfig config, String label, Bitmap icon) {
    424         if (!mEnableNotif) return;
    425         mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
    426 
    427         NotificationManager nm = (NotificationManager)
    428                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    429 
    430         if (nm != null) {
    431             String title = (label == null) ? mContext.getString(R.string.vpn_title) :
    432                     mContext.getString(R.string.vpn_title_long, label);
    433             String text = (config.session == null) ? mContext.getString(R.string.vpn_text) :
    434                     mContext.getString(R.string.vpn_text_long, config.session);
    435             config.startTime = SystemClock.elapsedRealtime();
    436 
    437             Notification notification = new Notification.Builder(mContext)
    438                     .setSmallIcon(R.drawable.vpn_connected)
    439                     .setLargeIcon(icon)
    440                     .setContentTitle(title)
    441                     .setContentText(text)
    442                     .setContentIntent(mStatusIntent)
    443                     .setDefaults(0)
    444                     .setOngoing(true)
    445                     .build();
    446             nm.notifyAsUser(null, R.drawable.vpn_connected, notification, UserHandle.ALL);
    447         }
    448     }
    449 
    450     private void hideNotification() {
    451         if (!mEnableNotif) return;
    452         mStatusIntent = null;
    453 
    454         NotificationManager nm = (NotificationManager)
    455                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    456 
    457         if (nm != null) {
    458             nm.cancelAsUser(null, R.drawable.vpn_connected, UserHandle.ALL);
    459         }
    460     }
    461 
    462     private native int jniCreate(int mtu);
    463     private native String jniGetName(int tun);
    464     private native int jniSetAddresses(String interfaze, String addresses);
    465     private native int jniSetRoutes(String interfaze, String routes);
    466     private native void jniReset(String interfaze);
    467     private native int jniCheck(String interfaze);
    468     private native void jniProtect(int socket, String interfaze);
    469 
    470     private static String findLegacyVpnGateway(LinkProperties prop) {
    471         for (RouteInfo route : prop.getRoutes()) {
    472             // Currently legacy VPN only works on IPv4.
    473             if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
    474                 return route.getGateway().getHostAddress();
    475             }
    476         }
    477 
    478         throw new IllegalStateException("Unable to find suitable gateway");
    479     }
    480 
    481     /**
    482      * Start legacy VPN, controlling native daemons as needed. Creates a
    483      * secondary thread to perform connection work, returning quickly.
    484      */
    485     public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
    486         enforceControlPermission();
    487         if (!keyStore.isUnlocked()) {
    488             throw new IllegalStateException("KeyStore isn't unlocked");
    489         }
    490 
    491         final String iface = egress.getInterfaceName();
    492         final String gateway = findLegacyVpnGateway(egress);
    493 
    494         // Load certificates.
    495         String privateKey = "";
    496         String userCert = "";
    497         String caCert = "";
    498         String serverCert = "";
    499         if (!profile.ipsecUserCert.isEmpty()) {
    500             privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
    501             byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
    502             userCert = (value == null) ? null : new String(value, Charsets.UTF_8);
    503         }
    504         if (!profile.ipsecCaCert.isEmpty()) {
    505             byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
    506             caCert = (value == null) ? null : new String(value, Charsets.UTF_8);
    507         }
    508         if (!profile.ipsecServerCert.isEmpty()) {
    509             byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
    510             serverCert = (value == null) ? null : new String(value, Charsets.UTF_8);
    511         }
    512         if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
    513             throw new IllegalStateException("Cannot load credentials");
    514         }
    515 
    516         // Prepare arguments for racoon.
    517         String[] racoon = null;
    518         switch (profile.type) {
    519             case VpnProfile.TYPE_L2TP_IPSEC_PSK:
    520                 racoon = new String[] {
    521                     iface, profile.server, "udppsk", profile.ipsecIdentifier,
    522                     profile.ipsecSecret, "1701",
    523                 };
    524                 break;
    525             case VpnProfile.TYPE_L2TP_IPSEC_RSA:
    526                 racoon = new String[] {
    527                     iface, profile.server, "udprsa", privateKey, userCert,
    528                     caCert, serverCert, "1701",
    529                 };
    530                 break;
    531             case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
    532                 racoon = new String[] {
    533                     iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
    534                     profile.ipsecSecret, profile.username, profile.password, "", gateway,
    535                 };
    536                 break;
    537             case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
    538                 racoon = new String[] {
    539                     iface, profile.server, "xauthrsa", privateKey, userCert,
    540                     caCert, serverCert, profile.username, profile.password, "", gateway,
    541                 };
    542                 break;
    543             case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
    544                 racoon = new String[] {
    545                     iface, profile.server, "hybridrsa",
    546                     caCert, serverCert, profile.username, profile.password, "", gateway,
    547                 };
    548                 break;
    549         }
    550 
    551         // Prepare arguments for mtpd.
    552         String[] mtpd = null;
    553         switch (profile.type) {
    554             case VpnProfile.TYPE_PPTP:
    555                 mtpd = new String[] {
    556                     iface, "pptp", profile.server, "1723",
    557                     "name", profile.username, "password", profile.password,
    558                     "linkname", "vpn", "refuse-eap", "nodefaultroute",
    559                     "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
    560                     (profile.mppe ? "+mppe" : "nomppe"),
    561                 };
    562                 break;
    563             case VpnProfile.TYPE_L2TP_IPSEC_PSK:
    564             case VpnProfile.TYPE_L2TP_IPSEC_RSA:
    565                 mtpd = new String[] {
    566                     iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
    567                     "name", profile.username, "password", profile.password,
    568                     "linkname", "vpn", "refuse-eap", "nodefaultroute",
    569                     "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
    570                 };
    571                 break;
    572         }
    573 
    574         VpnConfig config = new VpnConfig();
    575         config.legacy = true;
    576         config.user = profile.key;
    577         config.interfaze = iface;
    578         config.session = profile.name;
    579         config.routes = profile.routes;
    580         if (!profile.dnsServers.isEmpty()) {
    581             config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
    582         }
    583         if (!profile.searchDomains.isEmpty()) {
    584             config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
    585         }
    586         startLegacyVpn(config, racoon, mtpd);
    587     }
    588 
    589     private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
    590         stopLegacyVpn();
    591 
    592         // Prepare for the new request. This also checks the caller.
    593         prepare(null, VpnConfig.LEGACY_VPN);
    594         updateState(DetailedState.CONNECTING, "startLegacyVpn");
    595 
    596         // Start a new LegacyVpnRunner and we are done!
    597         mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
    598         mLegacyVpnRunner.start();
    599     }
    600 
    601     public synchronized void stopLegacyVpn() {
    602         if (mLegacyVpnRunner != null) {
    603             mLegacyVpnRunner.exit();
    604             mLegacyVpnRunner = null;
    605 
    606             synchronized (LegacyVpnRunner.TAG) {
    607                 // wait for old thread to completely finish before spinning up
    608                 // new instance, otherwise state updates can be out of order.
    609             }
    610         }
    611     }
    612 
    613     /**
    614      * Return the information of the current ongoing legacy VPN.
    615      */
    616     public synchronized LegacyVpnInfo getLegacyVpnInfo() {
    617         // Check if the caller is authorized.
    618         enforceControlPermission();
    619         if (mLegacyVpnRunner == null) return null;
    620 
    621         final LegacyVpnInfo info = new LegacyVpnInfo();
    622         info.key = mLegacyVpnRunner.mConfig.user;
    623         info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
    624         if (mNetworkInfo.isConnected()) {
    625             info.intent = mStatusIntent;
    626         }
    627         return info;
    628     }
    629 
    630     public VpnConfig getLegacyVpnConfig() {
    631         if (mLegacyVpnRunner != null) {
    632             return mLegacyVpnRunner.mConfig;
    633         } else {
    634             return null;
    635         }
    636     }
    637 
    638     /**
    639      * Bringing up a VPN connection takes time, and that is all this thread
    640      * does. Here we have plenty of time. The only thing we need to take
    641      * care of is responding to interruptions as soon as possible. Otherwise
    642      * requests will be piled up. This can be done in a Handler as a state
    643      * machine, but it is much easier to read in the current form.
    644      */
    645     private class LegacyVpnRunner extends Thread {
    646         private static final String TAG = "LegacyVpnRunner";
    647 
    648         private final VpnConfig mConfig;
    649         private final String[] mDaemons;
    650         private final String[][] mArguments;
    651         private final LocalSocket[] mSockets;
    652         private final String mOuterInterface;
    653         private final AtomicInteger mOuterConnection =
    654                 new AtomicInteger(ConnectivityManager.TYPE_NONE);
    655 
    656         private long mTimer = -1;
    657 
    658         /**
    659          * Watch for the outer connection (passing in the constructor) going away.
    660          */
    661         private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    662             @Override
    663             public void onReceive(Context context, Intent intent) {
    664                 if (!mEnableTeardown) return;
    665 
    666                 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
    667                     if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
    668                             ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
    669                         NetworkInfo info = (NetworkInfo)intent.getExtra(
    670                                 ConnectivityManager.EXTRA_NETWORK_INFO);
    671                         if (info != null && !info.isConnectedOrConnecting()) {
    672                             try {
    673                                 mObserver.interfaceStatusChanged(mOuterInterface, false);
    674                             } catch (RemoteException e) {}
    675                         }
    676                     }
    677                 }
    678             }
    679         };
    680 
    681         public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
    682             super(TAG);
    683             mConfig = config;
    684             mDaemons = new String[] {"racoon", "mtpd"};
    685             // TODO: clear arguments from memory once launched
    686             mArguments = new String[][] {racoon, mtpd};
    687             mSockets = new LocalSocket[mDaemons.length];
    688 
    689             // This is the interface which VPN is running on,
    690             // mConfig.interfaze will change to point to OUR
    691             // internal interface soon. TODO - add inner/outer to mconfig
    692             // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
    693             // we will leave the VPN up.  We should check that it's still there/connected after
    694             // registering
    695             mOuterInterface = mConfig.interfaze;
    696 
    697             try {
    698                 mOuterConnection.set(
    699                         mConnService.findConnectionTypeForIface(mOuterInterface));
    700             } catch (Exception e) {
    701                 mOuterConnection.set(ConnectivityManager.TYPE_NONE);
    702             }
    703 
    704             IntentFilter filter = new IntentFilter();
    705             filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    706             mContext.registerReceiver(mBroadcastReceiver, filter);
    707         }
    708 
    709         public void check(String interfaze) {
    710             if (interfaze.equals(mOuterInterface)) {
    711                 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
    712                 exit();
    713             }
    714         }
    715 
    716         public void exit() {
    717             // We assume that everything is reset after stopping the daemons.
    718             interrupt();
    719             for (LocalSocket socket : mSockets) {
    720                 IoUtils.closeQuietly(socket);
    721             }
    722             updateState(DetailedState.DISCONNECTED, "exit");
    723             try {
    724                 mContext.unregisterReceiver(mBroadcastReceiver);
    725             } catch (IllegalArgumentException e) {}
    726         }
    727 
    728         @Override
    729         public void run() {
    730             // Wait for the previous thread since it has been interrupted.
    731             Log.v(TAG, "Waiting");
    732             synchronized (TAG) {
    733                 Log.v(TAG, "Executing");
    734                 execute();
    735                 monitorDaemons();
    736             }
    737         }
    738 
    739         private void checkpoint(boolean yield) throws InterruptedException {
    740             long now = SystemClock.elapsedRealtime();
    741             if (mTimer == -1) {
    742                 mTimer = now;
    743                 Thread.sleep(1);
    744             } else if (now - mTimer <= 60000) {
    745                 Thread.sleep(yield ? 200 : 1);
    746             } else {
    747                 updateState(DetailedState.FAILED, "checkpoint");
    748                 throw new IllegalStateException("Time is up");
    749             }
    750         }
    751 
    752         private void execute() {
    753             // Catch all exceptions so we can clean up few things.
    754             boolean initFinished = false;
    755             try {
    756                 // Initialize the timer.
    757                 checkpoint(false);
    758 
    759                 // Wait for the daemons to stop.
    760                 for (String daemon : mDaemons) {
    761                     while (!SystemService.isStopped(daemon)) {
    762                         checkpoint(true);
    763                     }
    764                 }
    765 
    766                 // Clear the previous state.
    767                 File state = new File("/data/misc/vpn/state");
    768                 state.delete();
    769                 if (state.exists()) {
    770                     throw new IllegalStateException("Cannot delete the state");
    771                 }
    772                 new File("/data/misc/vpn/abort").delete();
    773                 initFinished = true;
    774 
    775                 // Check if we need to restart any of the daemons.
    776                 boolean restart = false;
    777                 for (String[] arguments : mArguments) {
    778                     restart = restart || (arguments != null);
    779                 }
    780                 if (!restart) {
    781                     updateState(DetailedState.DISCONNECTED, "execute");
    782                     return;
    783                 }
    784                 updateState(DetailedState.CONNECTING, "execute");
    785 
    786                 // Start the daemon with arguments.
    787                 for (int i = 0; i < mDaemons.length; ++i) {
    788                     String[] arguments = mArguments[i];
    789                     if (arguments == null) {
    790                         continue;
    791                     }
    792 
    793                     // Start the daemon.
    794                     String daemon = mDaemons[i];
    795                     SystemService.start(daemon);
    796 
    797                     // Wait for the daemon to start.
    798                     while (!SystemService.isRunning(daemon)) {
    799                         checkpoint(true);
    800                     }
    801 
    802                     // Create the control socket.
    803                     mSockets[i] = new LocalSocket();
    804                     LocalSocketAddress address = new LocalSocketAddress(
    805                             daemon, LocalSocketAddress.Namespace.RESERVED);
    806 
    807                     // Wait for the socket to connect.
    808                     while (true) {
    809                         try {
    810                             mSockets[i].connect(address);
    811                             break;
    812                         } catch (Exception e) {
    813                             // ignore
    814                         }
    815                         checkpoint(true);
    816                     }
    817                     mSockets[i].setSoTimeout(500);
    818 
    819                     // Send over the arguments.
    820                     OutputStream out = mSockets[i].getOutputStream();
    821                     for (String argument : arguments) {
    822                         byte[] bytes = argument.getBytes(Charsets.UTF_8);
    823                         if (bytes.length >= 0xFFFF) {
    824                             throw new IllegalArgumentException("Argument is too large");
    825                         }
    826                         out.write(bytes.length >> 8);
    827                         out.write(bytes.length);
    828                         out.write(bytes);
    829                         checkpoint(false);
    830                     }
    831                     out.write(0xFF);
    832                     out.write(0xFF);
    833                     out.flush();
    834 
    835                     // Wait for End-of-File.
    836                     InputStream in = mSockets[i].getInputStream();
    837                     while (true) {
    838                         try {
    839                             if (in.read() == -1) {
    840                                 break;
    841                             }
    842                         } catch (Exception e) {
    843                             // ignore
    844                         }
    845                         checkpoint(true);
    846                     }
    847                 }
    848 
    849                 // Wait for the daemons to create the new state.
    850                 while (!state.exists()) {
    851                     // Check if a running daemon is dead.
    852                     for (int i = 0; i < mDaemons.length; ++i) {
    853                         String daemon = mDaemons[i];
    854                         if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
    855                             throw new IllegalStateException(daemon + " is dead");
    856                         }
    857                     }
    858                     checkpoint(true);
    859                 }
    860 
    861                 // Now we are connected. Read and parse the new state.
    862                 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
    863                 if (parameters.length != 6) {
    864                     throw new IllegalStateException("Cannot parse the state");
    865                 }
    866 
    867                 // Set the interface and the addresses in the config.
    868                 mConfig.interfaze = parameters[0].trim();
    869                 mConfig.addresses = parameters[1].trim();
    870 
    871                 // Set the routes if they are not set in the config.
    872                 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
    873                     mConfig.routes = parameters[2].trim();
    874                 }
    875 
    876                 // Set the DNS servers if they are not set in the config.
    877                 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
    878                     String dnsServers = parameters[3].trim();
    879                     if (!dnsServers.isEmpty()) {
    880                         mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
    881                     }
    882                 }
    883 
    884                 // Set the search domains if they are not set in the config.
    885                 if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
    886                     String searchDomains = parameters[4].trim();
    887                     if (!searchDomains.isEmpty()) {
    888                         mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
    889                     }
    890                 }
    891 
    892                 // Set the routes.
    893                 jniSetRoutes(mConfig.interfaze, mConfig.routes);
    894 
    895                 // Here is the last step and it must be done synchronously.
    896                 synchronized (Vpn.this) {
    897                     // Check if the thread is interrupted while we are waiting.
    898                     checkpoint(false);
    899 
    900                     // Check if the interface is gone while we are waiting.
    901                     if (jniCheck(mConfig.interfaze) == 0) {
    902                         throw new IllegalStateException(mConfig.interfaze + " is gone");
    903                     }
    904 
    905                     // Now INetworkManagementEventObserver is watching our back.
    906                     mInterface = mConfig.interfaze;
    907                     mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
    908                     showNotification(mConfig, null, null);
    909 
    910                     Log.i(TAG, "Connected!");
    911                     updateState(DetailedState.CONNECTED, "execute");
    912                 }
    913             } catch (Exception e) {
    914                 Log.i(TAG, "Aborting", e);
    915                 exit();
    916             } finally {
    917                 // Kill the daemons if they fail to stop.
    918                 if (!initFinished) {
    919                     for (String daemon : mDaemons) {
    920                         SystemService.stop(daemon);
    921                     }
    922                 }
    923 
    924                 // Do not leave an unstable state.
    925                 if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
    926                     updateState(DetailedState.FAILED, "execute");
    927                 }
    928             }
    929         }
    930 
    931         /**
    932          * Monitor the daemons we started, moving to disconnected state if the
    933          * underlying services fail.
    934          */
    935         private void monitorDaemons() {
    936             if (!mNetworkInfo.isConnected()) {
    937                 return;
    938             }
    939 
    940             try {
    941                 while (true) {
    942                     Thread.sleep(2000);
    943                     for (int i = 0; i < mDaemons.length; i++) {
    944                         if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
    945                             return;
    946                         }
    947                     }
    948                 }
    949             } catch (InterruptedException e) {
    950                 Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
    951             } finally {
    952                 for (String daemon : mDaemons) {
    953                     SystemService.stop(daemon);
    954                 }
    955 
    956                 updateState(DetailedState.DISCONNECTED, "babysit");
    957             }
    958         }
    959     }
    960 }
    961