Vpn.java revision c2c0beab79a907f63e109eefe2a5aabcf2e3fd8f
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
17package com.android.server.connectivity;
18
19import static android.Manifest.permission.BIND_VPN_SERVICE;
20import static android.os.UserHandle.PER_USER_RANGE;
21import static android.net.RouteInfo.RTN_THROW;
22import static android.net.RouteInfo.RTN_UNREACHABLE;
23import static android.system.OsConstants.AF_INET;
24import static android.system.OsConstants.AF_INET6;
25
26import android.Manifest;
27import android.app.AppGlobals;
28import android.app.AppOpsManager;
29import android.app.PendingIntent;
30import android.content.BroadcastReceiver;
31import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.ServiceConnection;
36import android.content.pm.ApplicationInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.PackageManager.NameNotFoundException;
39import android.content.pm.ResolveInfo;
40import android.content.pm.UserInfo;
41import android.net.ConnectivityManager;
42import android.net.IConnectivityManager;
43import android.net.INetworkManagementEventObserver;
44import android.net.IpPrefix;
45import android.net.LinkAddress;
46import android.net.LinkProperties;
47import android.net.LocalSocket;
48import android.net.LocalSocketAddress;
49import android.net.Network;
50import android.net.NetworkAgent;
51import android.net.NetworkCapabilities;
52import android.net.NetworkInfo;
53import android.net.NetworkInfo.DetailedState;
54import android.net.NetworkMisc;
55import android.net.RouteInfo;
56import android.net.UidRange;
57import android.os.Binder;
58import android.os.FileUtils;
59import android.os.IBinder;
60import android.os.INetworkManagementService;
61import android.os.Looper;
62import android.os.Parcel;
63import android.os.ParcelFileDescriptor;
64import android.os.Process;
65import android.os.RemoteException;
66import android.os.SystemClock;
67import android.os.SystemService;
68import android.os.UserHandle;
69import android.os.UserManager;
70import android.security.Credentials;
71import android.security.KeyStore;
72import android.util.Log;
73
74import com.android.internal.annotations.GuardedBy;
75import com.android.internal.net.LegacyVpnInfo;
76import com.android.internal.net.VpnConfig;
77import com.android.internal.net.VpnProfile;
78import com.android.server.net.BaseNetworkObserver;
79
80import libcore.io.IoUtils;
81
82import java.io.File;
83import java.io.IOException;
84import java.io.InputStream;
85import java.io.OutputStream;
86import java.net.Inet4Address;
87import java.net.Inet6Address;
88import java.net.InetAddress;
89import java.nio.charset.StandardCharsets;
90import java.util.ArrayList;
91import java.util.Arrays;
92import java.util.List;
93import java.util.SortedSet;
94import java.util.TreeSet;
95import java.util.concurrent.atomic.AtomicInteger;
96
97/**
98 * @hide
99 */
100public class Vpn {
101    private static final String NETWORKTYPE = "VPN";
102    private static final String TAG = "Vpn";
103    private static final boolean LOGD = true;
104
105    // TODO: create separate trackers for each unique VPN to support
106    // automated reconnection
107
108    private Context mContext;
109    private NetworkInfo mNetworkInfo;
110    private String mPackage;
111    private int mOwnerUID;
112    private String mInterface;
113    private Connection mConnection;
114    private LegacyVpnRunner mLegacyVpnRunner;
115    private PendingIntent mStatusIntent;
116    private volatile boolean mEnableTeardown = true;
117    private final IConnectivityManager mConnService;
118    private final INetworkManagementService mNetd;
119    private VpnConfig mConfig;
120    private NetworkAgent mNetworkAgent;
121    private final Looper mLooper;
122    private final NetworkCapabilities mNetworkCapabilities;
123
124    /* list of users using this VPN. */
125    @GuardedBy("this")
126    private List<UidRange> mVpnUsers = null;
127    private BroadcastReceiver mUserIntentReceiver = null;
128
129    // Handle of user initiating VPN.
130    private final int mUserHandle;
131
132    public Vpn(Looper looper, Context context, INetworkManagementService netService,
133            IConnectivityManager connService, int userHandle) {
134        mContext = context;
135        mNetd = netService;
136        mConnService = connService;
137        mUserHandle = userHandle;
138        mLooper = looper;
139
140        mPackage = VpnConfig.LEGACY_VPN;
141        mOwnerUID = getAppUid(mPackage, mUserHandle);
142
143        try {
144            netService.registerObserver(mObserver);
145        } catch (RemoteException e) {
146            Log.wtf(TAG, "Problem registering observer", e);
147        }
148        if (userHandle == UserHandle.USER_OWNER) {
149            // Owner's VPN also needs to handle restricted users
150            mUserIntentReceiver = new BroadcastReceiver() {
151                @Override
152                public void onReceive(Context context, Intent intent) {
153                    final String action = intent.getAction();
154                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
155                            UserHandle.USER_NULL);
156                    if (userHandle == UserHandle.USER_NULL) return;
157
158                    if (Intent.ACTION_USER_ADDED.equals(action)) {
159                        onUserAdded(userHandle);
160                    } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
161                        onUserRemoved(userHandle);
162                    }
163                }
164            };
165
166            IntentFilter intentFilter = new IntentFilter();
167            intentFilter.addAction(Intent.ACTION_USER_ADDED);
168            intentFilter.addAction(Intent.ACTION_USER_REMOVED);
169            mContext.registerReceiverAsUser(
170                    mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
171        }
172
173        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
174        // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
175        mNetworkCapabilities = new NetworkCapabilities();
176        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
177        mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
178    }
179
180    /**
181     * Set if this object is responsible for watching for {@link NetworkInfo}
182     * teardown. When {@code false}, teardown is handled externally by someone
183     * else.
184     */
185    public void setEnableTeardown(boolean enableTeardown) {
186        mEnableTeardown = enableTeardown;
187    }
188
189    /**
190     * Update current state, dispaching event to listeners.
191     */
192    private void updateState(DetailedState detailedState, String reason) {
193        if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
194        mNetworkInfo.setDetailedState(detailedState, reason, null);
195        if (mNetworkAgent != null) {
196            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
197        }
198    }
199
200    /**
201     * Prepare for a VPN application. This method is designed to solve
202     * race conditions. It first compares the current prepared package
203     * with {@code oldPackage}. If they are the same, the prepared
204     * package is revoked and replaced with {@code newPackage}. If
205     * {@code oldPackage} is {@code null}, the comparison is omitted.
206     * If {@code newPackage} is the same package or {@code null}, the
207     * revocation is omitted. This method returns {@code true} if the
208     * operation is succeeded.
209     *
210     * Legacy VPN is handled specially since it is not a real package.
211     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
212     * it can be revoked by itself.
213     *
214     * @param oldPackage The package name of the old VPN application.
215     * @param newPackage The package name of the new VPN application.
216     * @return true if the operation is succeeded.
217     */
218    public synchronized boolean prepare(String oldPackage, String newPackage) {
219        // Return false if the package does not match.
220        if (oldPackage != null && getAppUid(oldPackage, mUserHandle) != mOwnerUID) {
221            // The package doesn't match. If this VPN was not previously authorized, return false
222            // to force user authorization. Otherwise, revoke the VPN anyway.
223            if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
224                long token = Binder.clearCallingIdentity();
225                try {
226                    // This looks bizarre, but it is what ConfirmDialog in VpnDialogs is doing when
227                    // the user clicks through to allow the VPN to consent. So we are emulating the
228                    // action of the dialog without actually showing it.
229                    prepare(null, oldPackage);
230                } finally {
231                    Binder.restoreCallingIdentity(token);
232                }
233                return true;
234            }
235            return false;
236        }
237
238        // Return true if we do not need to revoke.
239        if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
240                getAppUid(newPackage, mUserHandle) == mOwnerUID)) {
241            return true;
242        }
243
244        // Check if the caller is authorized.
245        enforceControlPermission();
246
247        // Reset the interface.
248        if (mInterface != null) {
249            mStatusIntent = null;
250            agentDisconnect();
251            jniReset(mInterface);
252            mInterface = null;
253            mVpnUsers = null;
254        }
255
256        // Revoke the connection or stop LegacyVpnRunner.
257        if (mConnection != null) {
258            try {
259                mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
260                        Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
261            } catch (Exception e) {
262                // ignore
263            }
264            mContext.unbindService(mConnection);
265            mConnection = null;
266        } else if (mLegacyVpnRunner != null) {
267            mLegacyVpnRunner.exit();
268            mLegacyVpnRunner = null;
269        }
270
271        long token = Binder.clearCallingIdentity();
272        try {
273            mNetd.denyProtect(mOwnerUID);
274        } catch (Exception e) {
275            Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
276        } finally {
277            Binder.restoreCallingIdentity(token);
278        }
279
280        Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
281        mPackage = newPackage;
282        mOwnerUID = getAppUid(newPackage, mUserHandle);
283        token = Binder.clearCallingIdentity();
284        try {
285            mNetd.allowProtect(mOwnerUID);
286        } catch (Exception e) {
287            Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
288        } finally {
289            Binder.restoreCallingIdentity(token);
290        }
291        mConfig = null;
292
293        updateState(DetailedState.IDLE, "prepare");
294        return true;
295    }
296
297    /**
298     * Set whether the current package has the ability to launch VPNs without user intervention.
299     */
300    public void setPackageAuthorization(boolean authorized) {
301        // Check if the caller is authorized.
302        enforceControlPermission();
303
304        if (mPackage == null || VpnConfig.LEGACY_VPN.equals(mPackage)) {
305            return;
306        }
307
308        long token = Binder.clearCallingIdentity();
309        try {
310            AppOpsManager appOps =
311                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
312            appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, mOwnerUID, mPackage,
313                    authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
314        } catch (Exception e) {
315            Log.wtf(TAG, "Failed to set app ops for package " + mPackage, e);
316        } finally {
317            Binder.restoreCallingIdentity(token);
318        }
319    }
320
321    private boolean isVpnUserPreConsented(String packageName) {
322        AppOpsManager appOps =
323                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
324
325        // Verify that the caller matches the given package and has permission to activate VPNs.
326        return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
327                packageName) == AppOpsManager.MODE_ALLOWED;
328    }
329
330    private int getAppUid(String app, int userHandle) {
331        if (VpnConfig.LEGACY_VPN.equals(app)) {
332            return Process.myUid();
333        }
334        PackageManager pm = mContext.getPackageManager();
335        int result;
336        try {
337            result = pm.getPackageUid(app, userHandle);
338        } catch (NameNotFoundException e) {
339            result = -1;
340        }
341        return result;
342    }
343
344    public NetworkInfo getNetworkInfo() {
345        return mNetworkInfo;
346    }
347
348    private LinkProperties makeLinkProperties() {
349        boolean allowIPv4 = mConfig.allowIPv4;
350        boolean allowIPv6 = mConfig.allowIPv6;
351
352        LinkProperties lp = new LinkProperties();
353
354        lp.setInterfaceName(mInterface);
355
356        if (mConfig.addresses != null) {
357            for (LinkAddress address : mConfig.addresses) {
358                lp.addLinkAddress(address);
359                allowIPv4 |= address.getAddress() instanceof Inet4Address;
360                allowIPv6 |= address.getAddress() instanceof Inet6Address;
361            }
362        }
363
364        if (mConfig.routes != null) {
365            for (RouteInfo route : mConfig.routes) {
366                lp.addRoute(route);
367                InetAddress address = route.getDestination().getAddress();
368                allowIPv4 |= address instanceof Inet4Address;
369                allowIPv6 |= address instanceof Inet6Address;
370            }
371        }
372
373        if (mConfig.dnsServers != null) {
374            for (String dnsServer : mConfig.dnsServers) {
375                InetAddress address = InetAddress.parseNumericAddress(dnsServer);
376                lp.addDnsServer(address);
377                allowIPv4 |= address instanceof Inet4Address;
378                allowIPv6 |= address instanceof Inet6Address;
379            }
380        }
381
382        if (!allowIPv4) {
383            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
384        }
385        if (!allowIPv6) {
386            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
387        }
388
389        // Concatenate search domains into a string.
390        StringBuilder buffer = new StringBuilder();
391        if (mConfig.searchDomains != null) {
392            for (String domain : mConfig.searchDomains) {
393                buffer.append(domain).append(' ');
394            }
395        }
396        lp.setDomains(buffer.toString().trim());
397
398        // TODO: Stop setting the MTU in jniCreate and set it here.
399
400        return lp;
401    }
402
403    private void agentConnect() {
404        LinkProperties lp = makeLinkProperties();
405
406        if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
407            mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
408        } else {
409            mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
410        }
411
412        mNetworkInfo.setIsAvailable(true);
413        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
414
415        NetworkMisc networkMisc = new NetworkMisc();
416        networkMisc.allowBypass = mConfig.allowBypass;
417
418        long token = Binder.clearCallingIdentity();
419        try {
420            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
421                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
422                            @Override
423                            public void unwanted() {
424                                // We are user controlled, not driven by NetworkRequest.
425                            }
426                        };
427        } finally {
428            Binder.restoreCallingIdentity(token);
429        }
430
431        addVpnUserLocked(mUserHandle);
432        // If we are owner assign all Restricted Users to this VPN
433        if (mUserHandle == UserHandle.USER_OWNER) {
434            token = Binder.clearCallingIdentity();
435            List<UserInfo> users;
436            try {
437                users = UserManager.get(mContext).getUsers();
438            } finally {
439                Binder.restoreCallingIdentity(token);
440            }
441            for (UserInfo user : users) {
442                if (user.isRestricted()) {
443                    addVpnUserLocked(user.id);
444                }
445            }
446        }
447        mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
448    }
449
450    private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
451        networkInfo.setIsAvailable(false);
452        networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
453        if (networkAgent != null) {
454            networkAgent.sendNetworkInfo(networkInfo);
455        }
456    }
457
458    private void agentDisconnect(NetworkAgent networkAgent) {
459        NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
460        agentDisconnect(networkInfo, networkAgent);
461    }
462
463    private void agentDisconnect() {
464        if (mNetworkInfo.isConnected()) {
465            agentDisconnect(mNetworkInfo, mNetworkAgent);
466            mNetworkAgent = null;
467        }
468    }
469
470    /**
471     * Establish a VPN network and return the file descriptor of the VPN
472     * interface. This methods returns {@code null} if the application is
473     * revoked or not prepared.
474     *
475     * @param config The parameters to configure the network.
476     * @return The file descriptor of the VPN interface.
477     */
478    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
479        // Check if the caller is already prepared.
480        UserManager mgr = UserManager.get(mContext);
481        if (Binder.getCallingUid() != mOwnerUID) {
482            return null;
483        }
484        // Check if the service is properly declared.
485        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
486        intent.setClassName(mPackage, config.user);
487        long token = Binder.clearCallingIdentity();
488        try {
489            // Restricted users are not allowed to create VPNs, they are tied to Owner
490            UserInfo user = mgr.getUserInfo(mUserHandle);
491            if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
492                throw new SecurityException("Restricted users cannot establish VPNs");
493            }
494
495            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
496                                                                        null, 0, mUserHandle);
497            if (info == null) {
498                throw new SecurityException("Cannot find " + config.user);
499            }
500            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
501                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
502            }
503        } catch (RemoteException e) {
504                throw new SecurityException("Cannot find " + config.user);
505        } finally {
506            Binder.restoreCallingIdentity(token);
507        }
508
509        // Save the old config in case we need to go back.
510        VpnConfig oldConfig = mConfig;
511        String oldInterface = mInterface;
512        Connection oldConnection = mConnection;
513        NetworkAgent oldNetworkAgent = mNetworkAgent;
514        mNetworkAgent = null;
515        List<UidRange> oldUsers = mVpnUsers;
516
517        // Configure the interface. Abort if any of these steps fails.
518        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
519        try {
520            updateState(DetailedState.CONNECTING, "establish");
521            String interfaze = jniGetName(tun.getFd());
522
523            // TEMP use the old jni calls until there is support for netd address setting
524            StringBuilder builder = new StringBuilder();
525            for (LinkAddress address : config.addresses) {
526                builder.append(" " + address);
527            }
528            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
529                throw new IllegalArgumentException("At least one address must be specified");
530            }
531            Connection connection = new Connection();
532            if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
533                        new UserHandle(mUserHandle))) {
534                throw new IllegalStateException("Cannot bind " + config.user);
535            }
536
537            mConnection = connection;
538            mInterface = interfaze;
539
540            // Fill more values.
541            config.user = mPackage;
542            config.interfaze = mInterface;
543            config.startTime = SystemClock.elapsedRealtime();
544            mConfig = config;
545
546            // Set up forwarding and DNS rules.
547            mVpnUsers = new ArrayList<UidRange>();
548
549            agentConnect();
550
551            if (oldConnection != null) {
552                mContext.unbindService(oldConnection);
553            }
554            // Remove the old tun's user forwarding rules
555            // The new tun's user rules have already been added so they will take over
556            // as rules are deleted. This prevents data leakage as the rules are moved over.
557            agentDisconnect(oldNetworkAgent);
558            if (oldInterface != null && !oldInterface.equals(interfaze)) {
559                jniReset(oldInterface);
560            }
561
562            try {
563                IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
564            } catch (IOException e) {
565                throw new IllegalStateException(
566                        "Cannot set tunnel's fd as blocking=" + config.blocking, e);
567            }
568        } catch (RuntimeException e) {
569            IoUtils.closeQuietly(tun);
570            agentDisconnect();
571            // restore old state
572            mConfig = oldConfig;
573            mConnection = oldConnection;
574            mVpnUsers = oldUsers;
575            mNetworkAgent = oldNetworkAgent;
576            mInterface = oldInterface;
577            throw e;
578        }
579        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
580        return tun;
581    }
582
583    private boolean isRunningLocked() {
584        return mNetworkAgent != null && mInterface != null;
585    }
586
587    // Returns true if the VPN has been established and the calling UID is its owner. Used to check
588    // that a call to mutate VPN state is admissible.
589    private boolean isCallerEstablishedOwnerLocked() {
590        return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
591    }
592
593    // Note: Return type guarantees results are deduped and sorted, which callers require.
594    private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
595        SortedSet<Integer> uids = new TreeSet<Integer>();
596        for (String app : packageNames) {
597            int uid = getAppUid(app, userHandle);
598            if (uid != -1) uids.add(uid);
599        }
600        return uids;
601    }
602
603    // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
604    private void addVpnUserLocked(int userHandle) {
605        if (mVpnUsers == null) {
606            throw new IllegalStateException("VPN is not active");
607        }
608
609        if (mConfig.allowedApplications != null) {
610            // Add ranges covering all UIDs for allowedApplications.
611            int start = -1, stop = -1;
612            for (int uid : getAppsUids(mConfig.allowedApplications, userHandle)) {
613                if (start == -1) {
614                    start = uid;
615                } else if (uid != stop + 1) {
616                    mVpnUsers.add(new UidRange(start, stop));
617                    start = uid;
618                }
619                stop = uid;
620            }
621            if (start != -1) mVpnUsers.add(new UidRange(start, stop));
622        } else if (mConfig.disallowedApplications != null) {
623            // Add all ranges for user skipping UIDs for disallowedApplications.
624            final UidRange userRange = UidRange.createForUser(userHandle);
625            int start = userRange.start;
626            for (int uid : getAppsUids(mConfig.disallowedApplications, userHandle)) {
627                if (uid == start) {
628                    start++;
629                } else {
630                    mVpnUsers.add(new UidRange(start, uid - 1));
631                    start = uid + 1;
632                }
633            }
634            if (start <= userRange.stop) mVpnUsers.add(new UidRange(start, userRange.stop));
635        } else {
636            // Add all UIDs for the user.
637            mVpnUsers.add(UidRange.createForUser(userHandle));
638        }
639
640        prepareStatusIntent();
641    }
642
643    // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
644    // apply to userHandle.
645    private List<UidRange> uidRangesForUser(int userHandle) {
646        final UidRange userRange = UidRange.createForUser(userHandle);
647        final List<UidRange> ranges = new ArrayList<UidRange>();
648        for (UidRange range : mVpnUsers) {
649            if (range.start >= userRange.start && range.stop <= userRange.stop) {
650                ranges.add(range);
651            }
652        }
653        return ranges;
654    }
655
656    private void removeVpnUserLocked(int userHandle) {
657        if (mVpnUsers == null) {
658            throw new IllegalStateException("VPN is not active");
659        }
660        final List<UidRange> ranges = uidRangesForUser(userHandle);
661        if (mNetworkAgent != null) {
662            mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
663        }
664        mVpnUsers.removeAll(ranges);
665        mStatusIntent = null;
666    }
667
668    private void onUserAdded(int userHandle) {
669        // If the user is restricted tie them to the owner's VPN
670        synchronized(Vpn.this) {
671            UserManager mgr = UserManager.get(mContext);
672            UserInfo user = mgr.getUserInfo(userHandle);
673            if (user.isRestricted()) {
674                try {
675                    addVpnUserLocked(userHandle);
676                    if (mNetworkAgent != null) {
677                        final List<UidRange> ranges = uidRangesForUser(userHandle);
678                        mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
679                    }
680                } catch (Exception e) {
681                    Log.wtf(TAG, "Failed to add restricted user to owner", e);
682                }
683            }
684        }
685    }
686
687    private void onUserRemoved(int userHandle) {
688        // clean up if restricted
689        synchronized(Vpn.this) {
690            UserManager mgr = UserManager.get(mContext);
691            UserInfo user = mgr.getUserInfo(userHandle);
692            if (user.isRestricted()) {
693                try {
694                    removeVpnUserLocked(userHandle);
695                } catch (Exception e) {
696                    Log.wtf(TAG, "Failed to remove restricted user to owner", e);
697                }
698            }
699        }
700    }
701
702    /**
703     * Return the configuration of the currently running VPN.
704     */
705    public VpnConfig getVpnConfig() {
706        enforceControlPermission();
707        return mConfig;
708    }
709
710    @Deprecated
711    public synchronized void interfaceStatusChanged(String iface, boolean up) {
712        try {
713            mObserver.interfaceStatusChanged(iface, up);
714        } catch (RemoteException e) {
715            // ignored; target is local
716        }
717    }
718
719    private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
720        @Override
721        public void interfaceStatusChanged(String interfaze, boolean up) {
722            synchronized (Vpn.this) {
723                if (!up && mLegacyVpnRunner != null) {
724                    mLegacyVpnRunner.check(interfaze);
725                }
726            }
727        }
728
729        @Override
730        public void interfaceRemoved(String interfaze) {
731            synchronized (Vpn.this) {
732                if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
733                    mStatusIntent = null;
734                    mVpnUsers = null;
735                    mInterface = null;
736                    if (mConnection != null) {
737                        mContext.unbindService(mConnection);
738                        mConnection = null;
739                        agentDisconnect();
740                    } else if (mLegacyVpnRunner != null) {
741                        mLegacyVpnRunner.exit();
742                        mLegacyVpnRunner = null;
743                    }
744                }
745            }
746        }
747    };
748
749    private void enforceControlPermission() {
750        mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
751    }
752
753    private class Connection implements ServiceConnection {
754        private IBinder mService;
755
756        @Override
757        public void onServiceConnected(ComponentName name, IBinder service) {
758            mService = service;
759        }
760
761        @Override
762        public void onServiceDisconnected(ComponentName name) {
763            mService = null;
764        }
765    }
766
767    private void prepareStatusIntent() {
768        final long token = Binder.clearCallingIdentity();
769        try {
770            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
771        } finally {
772            Binder.restoreCallingIdentity(token);
773        }
774    }
775
776    public synchronized boolean addAddress(String address, int prefixLength) {
777        if (!isCallerEstablishedOwnerLocked()) {
778            return false;
779        }
780        boolean success = jniAddAddress(mInterface, address, prefixLength);
781        mNetworkAgent.sendLinkProperties(makeLinkProperties());
782        return success;
783    }
784
785    public synchronized boolean removeAddress(String address, int prefixLength) {
786        if (!isCallerEstablishedOwnerLocked()) {
787            return false;
788        }
789        boolean success = jniDelAddress(mInterface, address, prefixLength);
790        mNetworkAgent.sendLinkProperties(makeLinkProperties());
791        return success;
792    }
793
794    public synchronized boolean setUnderlyingNetworks(Network[] networks) {
795        if (!isCallerEstablishedOwnerLocked()) {
796            return false;
797        }
798        if (networks == null) {
799            mConfig.underlyingNetworks = null;
800        } else {
801            mConfig.underlyingNetworks = new Network[networks.length];
802            for (int i = 0; i < networks.length; ++i) {
803                if (networks[i] == null) {
804                    mConfig.underlyingNetworks[i] = null;
805                } else {
806                    mConfig.underlyingNetworks[i] = new Network(networks[i].netId);
807                }
808            }
809        }
810        return true;
811    }
812
813    public synchronized Network[] getUnderlyingNetworks() {
814        if (!isRunningLocked()) {
815            return null;
816        }
817        return mConfig.underlyingNetworks;
818    }
819
820    public synchronized boolean appliesToUid(int uid) {
821        if (!isRunningLocked()) {
822            return false;
823        }
824        for (UidRange uidRange : mVpnUsers) {
825            if (uidRange.start <= uid && uid <= uidRange.stop) {
826                return true;
827            }
828        }
829        return false;
830    }
831
832    private native int jniCreate(int mtu);
833    private native String jniGetName(int tun);
834    private native int jniSetAddresses(String interfaze, String addresses);
835    private native void jniReset(String interfaze);
836    private native int jniCheck(String interfaze);
837    private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
838    private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
839
840    private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
841        for (RouteInfo route : prop.getAllRoutes()) {
842            // Currently legacy VPN only works on IPv4.
843            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
844                return route;
845            }
846        }
847
848        throw new IllegalStateException("Unable to find IPv4 default gateway");
849    }
850
851    /**
852     * Start legacy VPN, controlling native daemons as needed. Creates a
853     * secondary thread to perform connection work, returning quickly.
854     */
855    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
856        enforceControlPermission();
857        if (!keyStore.isUnlocked()) {
858            throw new IllegalStateException("KeyStore isn't unlocked");
859        }
860        UserManager mgr = UserManager.get(mContext);
861        UserInfo user = mgr.getUserInfo(mUserHandle);
862        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
863            throw new SecurityException("Restricted users cannot establish VPNs");
864        }
865
866        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
867        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
868        final String iface = ipv4DefaultRoute.getInterface();
869
870        // Load certificates.
871        String privateKey = "";
872        String userCert = "";
873        String caCert = "";
874        String serverCert = "";
875        if (!profile.ipsecUserCert.isEmpty()) {
876            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
877            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
878            userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
879        }
880        if (!profile.ipsecCaCert.isEmpty()) {
881            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
882            caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
883        }
884        if (!profile.ipsecServerCert.isEmpty()) {
885            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
886            serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
887        }
888        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
889            throw new IllegalStateException("Cannot load credentials");
890        }
891
892        // Prepare arguments for racoon.
893        String[] racoon = null;
894        switch (profile.type) {
895            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
896                racoon = new String[] {
897                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
898                    profile.ipsecSecret, "1701",
899                };
900                break;
901            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
902                racoon = new String[] {
903                    iface, profile.server, "udprsa", privateKey, userCert,
904                    caCert, serverCert, "1701",
905                };
906                break;
907            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
908                racoon = new String[] {
909                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
910                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
911                };
912                break;
913            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
914                racoon = new String[] {
915                    iface, profile.server, "xauthrsa", privateKey, userCert,
916                    caCert, serverCert, profile.username, profile.password, "", gateway,
917                };
918                break;
919            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
920                racoon = new String[] {
921                    iface, profile.server, "hybridrsa",
922                    caCert, serverCert, profile.username, profile.password, "", gateway,
923                };
924                break;
925        }
926
927        // Prepare arguments for mtpd.
928        String[] mtpd = null;
929        switch (profile.type) {
930            case VpnProfile.TYPE_PPTP:
931                mtpd = new String[] {
932                    iface, "pptp", profile.server, "1723",
933                    "name", profile.username, "password", profile.password,
934                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
935                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
936                    (profile.mppe ? "+mppe" : "nomppe"),
937                };
938                break;
939            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
940            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
941                mtpd = new String[] {
942                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
943                    "name", profile.username, "password", profile.password,
944                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
945                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
946                };
947                break;
948        }
949
950        VpnConfig config = new VpnConfig();
951        config.legacy = true;
952        config.user = profile.key;
953        config.interfaze = iface;
954        config.session = profile.name;
955
956        config.addLegacyRoutes(profile.routes);
957        if (!profile.dnsServers.isEmpty()) {
958            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
959        }
960        if (!profile.searchDomains.isEmpty()) {
961            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
962        }
963        startLegacyVpn(config, racoon, mtpd);
964    }
965
966    private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
967        stopLegacyVpn();
968
969        // Prepare for the new request. This also checks the caller.
970        prepare(null, VpnConfig.LEGACY_VPN);
971        updateState(DetailedState.CONNECTING, "startLegacyVpn");
972
973        // Start a new LegacyVpnRunner and we are done!
974        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
975        mLegacyVpnRunner.start();
976    }
977
978    public synchronized void stopLegacyVpn() {
979        if (mLegacyVpnRunner != null) {
980            mLegacyVpnRunner.exit();
981            mLegacyVpnRunner = null;
982
983            synchronized (LegacyVpnRunner.TAG) {
984                // wait for old thread to completely finish before spinning up
985                // new instance, otherwise state updates can be out of order.
986            }
987        }
988    }
989
990    /**
991     * Return the information of the current ongoing legacy VPN.
992     */
993    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
994        // Check if the caller is authorized.
995        enforceControlPermission();
996        if (mLegacyVpnRunner == null) return null;
997
998        final LegacyVpnInfo info = new LegacyVpnInfo();
999        info.key = mConfig.user;
1000        info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
1001        if (mNetworkInfo.isConnected()) {
1002            info.intent = mStatusIntent;
1003        }
1004        return info;
1005    }
1006
1007    public VpnConfig getLegacyVpnConfig() {
1008        if (mLegacyVpnRunner != null) {
1009            return mConfig;
1010        } else {
1011            return null;
1012        }
1013    }
1014
1015    /**
1016     * Bringing up a VPN connection takes time, and that is all this thread
1017     * does. Here we have plenty of time. The only thing we need to take
1018     * care of is responding to interruptions as soon as possible. Otherwise
1019     * requests will be piled up. This can be done in a Handler as a state
1020     * machine, but it is much easier to read in the current form.
1021     */
1022    private class LegacyVpnRunner extends Thread {
1023        private static final String TAG = "LegacyVpnRunner";
1024
1025        private final String[] mDaemons;
1026        private final String[][] mArguments;
1027        private final LocalSocket[] mSockets;
1028        private final String mOuterInterface;
1029        private final AtomicInteger mOuterConnection =
1030                new AtomicInteger(ConnectivityManager.TYPE_NONE);
1031
1032        private long mTimer = -1;
1033
1034        /**
1035         * Watch for the outer connection (passing in the constructor) going away.
1036         */
1037        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1038            @Override
1039            public void onReceive(Context context, Intent intent) {
1040                if (!mEnableTeardown) return;
1041
1042                if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1043                    if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
1044                            ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
1045                        NetworkInfo info = (NetworkInfo)intent.getExtra(
1046                                ConnectivityManager.EXTRA_NETWORK_INFO);
1047                        if (info != null && !info.isConnectedOrConnecting()) {
1048                            try {
1049                                mObserver.interfaceStatusChanged(mOuterInterface, false);
1050                            } catch (RemoteException e) {}
1051                        }
1052                    }
1053                }
1054            }
1055        };
1056
1057        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
1058            super(TAG);
1059            mConfig = config;
1060            mDaemons = new String[] {"racoon", "mtpd"};
1061            // TODO: clear arguments from memory once launched
1062            mArguments = new String[][] {racoon, mtpd};
1063            mSockets = new LocalSocket[mDaemons.length];
1064
1065            // This is the interface which VPN is running on,
1066            // mConfig.interfaze will change to point to OUR
1067            // internal interface soon. TODO - add inner/outer to mconfig
1068            // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
1069            // we will leave the VPN up.  We should check that it's still there/connected after
1070            // registering
1071            mOuterInterface = mConfig.interfaze;
1072
1073            try {
1074                mOuterConnection.set(
1075                        mConnService.findConnectionTypeForIface(mOuterInterface));
1076            } catch (Exception e) {
1077                mOuterConnection.set(ConnectivityManager.TYPE_NONE);
1078            }
1079
1080            IntentFilter filter = new IntentFilter();
1081            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
1082            mContext.registerReceiver(mBroadcastReceiver, filter);
1083        }
1084
1085        public void check(String interfaze) {
1086            if (interfaze.equals(mOuterInterface)) {
1087                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
1088                exit();
1089            }
1090        }
1091
1092        public void exit() {
1093            // We assume that everything is reset after stopping the daemons.
1094            interrupt();
1095            for (LocalSocket socket : mSockets) {
1096                IoUtils.closeQuietly(socket);
1097            }
1098            agentDisconnect();
1099            try {
1100                mContext.unregisterReceiver(mBroadcastReceiver);
1101            } catch (IllegalArgumentException e) {}
1102        }
1103
1104        @Override
1105        public void run() {
1106            // Wait for the previous thread since it has been interrupted.
1107            Log.v(TAG, "Waiting");
1108            synchronized (TAG) {
1109                Log.v(TAG, "Executing");
1110                execute();
1111                monitorDaemons();
1112            }
1113        }
1114
1115        private void checkpoint(boolean yield) throws InterruptedException {
1116            long now = SystemClock.elapsedRealtime();
1117            if (mTimer == -1) {
1118                mTimer = now;
1119                Thread.sleep(1);
1120            } else if (now - mTimer <= 60000) {
1121                Thread.sleep(yield ? 200 : 1);
1122            } else {
1123                updateState(DetailedState.FAILED, "checkpoint");
1124                throw new IllegalStateException("Time is up");
1125            }
1126        }
1127
1128        private void execute() {
1129            // Catch all exceptions so we can clean up few things.
1130            boolean initFinished = false;
1131            try {
1132                // Initialize the timer.
1133                checkpoint(false);
1134
1135                // Wait for the daemons to stop.
1136                for (String daemon : mDaemons) {
1137                    while (!SystemService.isStopped(daemon)) {
1138                        checkpoint(true);
1139                    }
1140                }
1141
1142                // Clear the previous state.
1143                File state = new File("/data/misc/vpn/state");
1144                state.delete();
1145                if (state.exists()) {
1146                    throw new IllegalStateException("Cannot delete the state");
1147                }
1148                new File("/data/misc/vpn/abort").delete();
1149                initFinished = true;
1150
1151                // Check if we need to restart any of the daemons.
1152                boolean restart = false;
1153                for (String[] arguments : mArguments) {
1154                    restart = restart || (arguments != null);
1155                }
1156                if (!restart) {
1157                    agentDisconnect();
1158                    return;
1159                }
1160                updateState(DetailedState.CONNECTING, "execute");
1161
1162                // Start the daemon with arguments.
1163                for (int i = 0; i < mDaemons.length; ++i) {
1164                    String[] arguments = mArguments[i];
1165                    if (arguments == null) {
1166                        continue;
1167                    }
1168
1169                    // Start the daemon.
1170                    String daemon = mDaemons[i];
1171                    SystemService.start(daemon);
1172
1173                    // Wait for the daemon to start.
1174                    while (!SystemService.isRunning(daemon)) {
1175                        checkpoint(true);
1176                    }
1177
1178                    // Create the control socket.
1179                    mSockets[i] = new LocalSocket();
1180                    LocalSocketAddress address = new LocalSocketAddress(
1181                            daemon, LocalSocketAddress.Namespace.RESERVED);
1182
1183                    // Wait for the socket to connect.
1184                    while (true) {
1185                        try {
1186                            mSockets[i].connect(address);
1187                            break;
1188                        } catch (Exception e) {
1189                            // ignore
1190                        }
1191                        checkpoint(true);
1192                    }
1193                    mSockets[i].setSoTimeout(500);
1194
1195                    // Send over the arguments.
1196                    OutputStream out = mSockets[i].getOutputStream();
1197                    for (String argument : arguments) {
1198                        byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
1199                        if (bytes.length >= 0xFFFF) {
1200                            throw new IllegalArgumentException("Argument is too large");
1201                        }
1202                        out.write(bytes.length >> 8);
1203                        out.write(bytes.length);
1204                        out.write(bytes);
1205                        checkpoint(false);
1206                    }
1207                    out.write(0xFF);
1208                    out.write(0xFF);
1209                    out.flush();
1210
1211                    // Wait for End-of-File.
1212                    InputStream in = mSockets[i].getInputStream();
1213                    while (true) {
1214                        try {
1215                            if (in.read() == -1) {
1216                                break;
1217                            }
1218                        } catch (Exception e) {
1219                            // ignore
1220                        }
1221                        checkpoint(true);
1222                    }
1223                }
1224
1225                // Wait for the daemons to create the new state.
1226                while (!state.exists()) {
1227                    // Check if a running daemon is dead.
1228                    for (int i = 0; i < mDaemons.length; ++i) {
1229                        String daemon = mDaemons[i];
1230                        if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
1231                            throw new IllegalStateException(daemon + " is dead");
1232                        }
1233                    }
1234                    checkpoint(true);
1235                }
1236
1237                // Now we are connected. Read and parse the new state.
1238                String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
1239                if (parameters.length != 7) {
1240                    throw new IllegalStateException("Cannot parse the state");
1241                }
1242
1243                // Set the interface and the addresses in the config.
1244                mConfig.interfaze = parameters[0].trim();
1245
1246                mConfig.addLegacyAddresses(parameters[1]);
1247                // Set the routes if they are not set in the config.
1248                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
1249                    mConfig.addLegacyRoutes(parameters[2]);
1250                }
1251
1252                // Set the DNS servers if they are not set in the config.
1253                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
1254                    String dnsServers = parameters[3].trim();
1255                    if (!dnsServers.isEmpty()) {
1256                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
1257                    }
1258                }
1259
1260                // Set the search domains if they are not set in the config.
1261                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
1262                    String searchDomains = parameters[4].trim();
1263                    if (!searchDomains.isEmpty()) {
1264                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
1265                    }
1266                }
1267
1268                // Add a throw route for the VPN server endpoint, if one was specified.
1269                String endpoint = parameters[5];
1270                if (!endpoint.isEmpty()) {
1271                    try {
1272                        InetAddress addr = InetAddress.parseNumericAddress(endpoint);
1273                        if (addr instanceof Inet4Address) {
1274                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
1275                        } else if (addr instanceof Inet6Address) {
1276                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
1277                        } else {
1278                            Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
1279                        }
1280                    } catch (IllegalArgumentException e) {
1281                        Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
1282                    }
1283                }
1284
1285                // Here is the last step and it must be done synchronously.
1286                synchronized (Vpn.this) {
1287                    // Set the start time
1288                    mConfig.startTime = SystemClock.elapsedRealtime();
1289
1290                    // Check if the thread is interrupted while we are waiting.
1291                    checkpoint(false);
1292
1293                    // Check if the interface is gone while we are waiting.
1294                    if (jniCheck(mConfig.interfaze) == 0) {
1295                        throw new IllegalStateException(mConfig.interfaze + " is gone");
1296                    }
1297
1298                    // Now INetworkManagementEventObserver is watching our back.
1299                    mInterface = mConfig.interfaze;
1300                    mVpnUsers = new ArrayList<UidRange>();
1301
1302                    agentConnect();
1303
1304                    Log.i(TAG, "Connected!");
1305                }
1306            } catch (Exception e) {
1307                Log.i(TAG, "Aborting", e);
1308                updateState(DetailedState.FAILED, e.getMessage());
1309                exit();
1310            } finally {
1311                // Kill the daemons if they fail to stop.
1312                if (!initFinished) {
1313                    for (String daemon : mDaemons) {
1314                        SystemService.stop(daemon);
1315                    }
1316                }
1317
1318                // Do not leave an unstable state.
1319                if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
1320                    agentDisconnect();
1321                }
1322            }
1323        }
1324
1325        /**
1326         * Monitor the daemons we started, moving to disconnected state if the
1327         * underlying services fail.
1328         */
1329        private void monitorDaemons() {
1330            if (!mNetworkInfo.isConnected()) {
1331                return;
1332            }
1333
1334            try {
1335                while (true) {
1336                    Thread.sleep(2000);
1337                    for (int i = 0; i < mDaemons.length; i++) {
1338                        if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1339                            return;
1340                        }
1341                    }
1342                }
1343            } catch (InterruptedException e) {
1344                Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
1345            } finally {
1346                for (String daemon : mDaemons) {
1347                    SystemService.stop(daemon);
1348                }
1349
1350                agentDisconnect();
1351            }
1352        }
1353    }
1354}
1355