Vpn.java revision 8cd33ed84e94036a5e1201485af7603dc6fb0d9b
1ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/*
2ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Copyright (C) 2011 The Android Open Source Project
3ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
4ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License");
5ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * you may not use this file except in compliance with the License.
6ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * You may obtain a copy of the License at
7ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
8ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *      http://www.apache.org/licenses/LICENSE-2.0
9ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
10ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Unless required by applicable law or agreed to in writing, software
11ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS,
12ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * See the License for the specific language governing permissions and
14ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * limitations under the License.
15ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
16ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
17ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehpackage com.android.server.connectivity;
18ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
19899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport static android.Manifest.permission.BIND_VPN_SERVICE;
20899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
214ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.app.AppGlobals;
22ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.app.Notification;
23ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.app.NotificationManager;
24899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.app.PendingIntent;
251b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.content.BroadcastReceiver;
26199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ComponentName;
27ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Context;
28ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Intent;
291b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.content.IntentFilter;
30199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ServiceConnection;
31ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.ApplicationInfo;
32ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.PackageManager;
336bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.content.pm.PackageManager.NameNotFoundException;
34199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.pm.ResolveInfo;
35c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.content.pm.UserInfo;
36ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.Bitmap;
37ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.Canvas;
38ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.drawable.Drawable;
39899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.BaseNetworkStateTracker;
40899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.ConnectivityManager;
411b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.net.IConnectivityManager;
42ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.net.INetworkManagementEventObserver;
434ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.net.LinkAddress;
4482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.LinkProperties;
4585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocket;
4685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocketAddress;
476bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkAgent;
486bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkCapabilities;
49899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.NetworkInfo;
506bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkInfo.DetailedState;
518cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandranimport android.net.NetworkMisc;
52c023453a2b79b338aea36b48fd610a099379d34cChad Brubakerimport android.net.NetworkUtils;
5382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.RouteInfo;
546bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.UidRange;
55ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.Binder;
56c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yehimport android.os.FileUtils;
57199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.IBinder;
58899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.INetworkManagementService;
596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.os.Looper;
60199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.Parcel;
61ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.ParcelFileDescriptor;
6285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.Process;
63899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.RemoteException;
6485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemClock;
65088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkeyimport android.os.SystemService;
6650cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackbornimport android.os.UserHandle;
67c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.os.UserManager;
6882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.Credentials;
6982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.KeyStore;
70ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.util.Log;
71c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.util.SparseBooleanArray;
72ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
73c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport com.android.internal.annotations.GuardedBy;
74ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport com.android.internal.R;
752e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
7604ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig;
7782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport com.android.internal.net.VpnProfile;
78899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport com.android.server.net.BaseNetworkObserver;
79ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
8097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.File;
816bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidsonimport java.io.IOException;
8297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.InputStream;
8385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream;
84c023453a2b79b338aea36b48fd610a099379d34cChad Brubakerimport java.net.InetAddress;
8582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport java.net.Inet4Address;
86d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets;
876bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.ArrayList;
8841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yehimport java.util.Arrays;
896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.List;
901b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport java.util.concurrent.atomic.AtomicInteger;
9185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
92065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkeyimport libcore.io.IoUtils;
93065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey
94ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/**
95ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * @hide
96ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
976bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenpublic class Vpn {
986bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private static final String NETWORKTYPE = "VPN";
99899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private static final String TAG = "Vpn";
100899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private static final boolean LOGD = true;
1016bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
102899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    // TODO: create separate trackers for each unique VPN to support
103899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    // automated reconnection
104199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
1056bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private Context mContext;
1066bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkInfo mNetworkInfo;
1076bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private String mPackage;
1086bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private int mOwnerUID;
109c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private String mInterface;
110199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private Connection mConnection;
11185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private LegacyVpnRunner mLegacyVpnRunner;
112899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private PendingIntent mStatusIntent;
11357666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    private volatile boolean mEnableNotif = true;
11457666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    private volatile boolean mEnableTeardown = true;
1151b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt    private final IConnectivityManager mConnService;
1166bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final INetworkManagementService mNetd;
117c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private VpnConfig mConfig;
1186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkAgent mNetworkAgent;
1196bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final Looper mLooper;
1206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final NetworkCapabilities mNetworkCapabilities;
121c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
122c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    /* list of users using this VPN. */
123c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    @GuardedBy("this")
1246bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private List<UidRange> mVpnUsers = null;
125c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private BroadcastReceiver mUserIntentReceiver = null;
126c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
1274ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker    private final int mUserId;
128ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
1296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public Vpn(Looper looper, Context context, INetworkManagementService netService,
1304ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            IConnectivityManager connService, int userId) {
131ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext = context;
1326bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetd = netService;
1331b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        mConnService = connService;
1344ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        mUserId = userId;
1356bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mLooper = looper;
1366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
1376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mPackage = VpnConfig.LEGACY_VPN;
1386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mOwnerUID = getAppUid(mPackage);
139899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
140899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
141899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            netService.registerObserver(mObserver);
142899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
143899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            Log.wtf(TAG, "Problem registering observer", e);
144899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
145c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        if (userId == UserHandle.USER_OWNER) {
146c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Owner's VPN also needs to handle restricted users
147c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mUserIntentReceiver = new BroadcastReceiver() {
148c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                @Override
149c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                public void onReceive(Context context, Intent intent) {
150c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    final String action = intent.getAction();
151c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
152c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                            UserHandle.USER_NULL);
153c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    if (userId == UserHandle.USER_NULL) return;
154c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
155c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    if (Intent.ACTION_USER_ADDED.equals(action)) {
156c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                        onUserAdded(userId);
157c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
158c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                        onUserRemoved(userId);
159c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    }
160c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
161c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            };
162c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
163c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            IntentFilter intentFilter = new IntentFilter();
164c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            intentFilter.addAction(Intent.ACTION_USER_ADDED);
165c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            intentFilter.addAction(Intent.ACTION_USER_REMOVED);
166c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mContext.registerReceiverAsUser(
167c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
168c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
1696bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
1706bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
1716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
1726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities = new NetworkCapabilities();
1736bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
1746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
175899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
176899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
17757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    /**
17857666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * Set if this object is responsible for showing its own notifications. When
17957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * {@code false}, notifications are handled externally by someone else.
18057666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     */
18169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public void setEnableNotifications(boolean enableNotif) {
18269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        mEnableNotif = enableNotif;
18369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
18469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
18557666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    /**
18657666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * Set if this object is responsible for watching for {@link NetworkInfo}
18757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * teardown. When {@code false}, teardown is handled externally by someone
18857666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * else.
18957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     */
19057666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    public void setEnableTeardown(boolean enableTeardown) {
19157666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mEnableTeardown = enableTeardown;
19257666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    }
19357666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
194899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    /**
195899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     * Update current state, dispaching event to listeners.
196899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     */
197899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private void updateState(DetailedState detailedState, String reason) {
198899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
199899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        mNetworkInfo.setDetailedState(detailedState, reason, null);
2006bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkAgent != null) {
2016bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
2026bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
203ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
204ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
205ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
206100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Prepare for a VPN application. This method is designed to solve
207100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * race conditions. It first compares the current prepared package
208100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * with {@code oldPackage}. If they are the same, the prepared
209100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * package is revoked and replaced with {@code newPackage}. If
210100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * {@code oldPackage} is {@code null}, the comparison is omitted.
211100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * If {@code newPackage} is the same package or {@code null}, the
212100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revocation is omitted. This method returns {@code true} if the
213100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * operation is succeeded.
214e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     *
215100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Legacy VPN is handled specially since it is not a real package.
216100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
217100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * it can be revoked by itself.
218100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     *
219100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param oldPackage The package name of the old VPN application.
220100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param newPackage The package name of the new VPN application.
221100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @return true if the operation is succeeded.
222ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
223100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh    public synchronized boolean prepare(String oldPackage, String newPackage) {
224100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return false if the package does not match.
225c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (oldPackage != null && !oldPackage.equals(mPackage)) {
226100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return false;
227100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        }
228100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh
229100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return true if we do not need to revoke.
230100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        if (newPackage == null ||
231c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
232100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return true;
233ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
234ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
235dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // Check if the caller is authorized.
236dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
2377b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh
238ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Reset the interface and hide the notification.
239c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (mInterface != null) {
2406bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (UidRange uidRange : mVpnUsers) {
2416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                hideNotification(uidRange.getStartUser());
242899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
2436bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
2444ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            jniReset(mInterface);
245c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = null;
246c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mVpnUsers = null;
247ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
248ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
249fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Revoke the connection or stop LegacyVpnRunner.
250199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        if (mConnection != null) {
251199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            try {
252199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
253199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                        Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
254199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            } catch (Exception e) {
255199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                // ignore
256199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
257199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mContext.unbindService(mConnection);
258199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = null;
259e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        } else if (mLegacyVpnRunner != null) {
26041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mLegacyVpnRunner.exit();
26141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mLegacyVpnRunner = null;
26241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
26341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
2646bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        long token = Binder.clearCallingIdentity();
2656bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
2666bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetd.denyProtect(mOwnerUID);
2676bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (Exception e) {
2686bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
2696bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } finally {
2706bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Binder.restoreCallingIdentity(token);
2716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
2726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
273c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
274c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        mPackage = newPackage;
2756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mOwnerUID = getAppUid(newPackage);
2766bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        token = Binder.clearCallingIdentity();
2776bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
2786bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetd.allowProtect(mOwnerUID);
2796bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (Exception e) {
2806bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
2816bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } finally {
2826bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Binder.restoreCallingIdentity(token);
2836bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
284c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        mConfig = null;
285899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        updateState(DetailedState.IDLE, "prepare");
286100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        return true;
287ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
288ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
2896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private int getAppUid(String app) {
2906bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (app == VpnConfig.LEGACY_VPN) {
2916bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            return Process.myUid();
2926bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
293fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
2946bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        int result;
2956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
2966bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            result = pm.getPackageUid(app, mUserId);
2976bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (NameNotFoundException e) {
2986bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            result = -1;
299fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        }
3006bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return result;
3016bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3026bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
3036bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public NetworkInfo getNetworkInfo() {
3046bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return mNetworkInfo;
3056bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3066bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
3076bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentConnect() {
3086bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        LinkProperties lp = new LinkProperties();
3096bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setInterfaceName(mInterface);
3106bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        boolean hasDefaultRoute = false;
3116bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        for (RouteInfo route : mConfig.routes) {
3126bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            lp.addRoute(route);
3136bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            if (route.isDefaultRoute()) hasDefaultRoute = true;
3146bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
3156bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (hasDefaultRoute) {
3166bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
3176bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } else {
3186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
3196bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
3206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.dnsServers != null) {
3216bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String dnsServer : mConfig.dnsServers) {
3226bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                lp.addDnsServer(InetAddress.parseNumericAddress(dnsServer));
3236bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
3246bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
3256bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // Concatenate search domains into a string.
3266bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        StringBuilder buffer = new StringBuilder();
3276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.searchDomains != null) {
3286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String domain : mConfig.searchDomains) {
3296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                buffer.append(domain).append(' ');
3306bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
3316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
3326bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setDomains(buffer.toString().trim());
3336bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo.setIsAvailable(true);
3346bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
3358cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran        NetworkMisc networkMisc = new NetworkMisc();
3368cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran        if (mConfig.allowBypass) {
3378cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran            networkMisc.allowBypass = true;
3388cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran        }
3396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        long token = Binder.clearCallingIdentity();
3404ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
3416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
3428cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
3436bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                            public void unwanted() {
3446bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                                // We are user controlled, not driven by NetworkRequest.
3456bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                            };
3466bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        };
3474ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
3484ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
3494ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        }
3506bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        addVpnUserLocked(mUserId);
3516bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // If we are owner assign all Restricted Users to this VPN
3526bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mUserId == UserHandle.USER_OWNER) {
3536bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            token = Binder.clearCallingIdentity();
3546bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            List<UserInfo> users;
3556bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            try {
3566bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                users = UserManager.get(mContext).getUsers();
3576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            } finally {
3586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                Binder.restoreCallingIdentity(token);
3596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
3606bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (UserInfo user : users) {
3616bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                if (user.isRestricted()) {
3626bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    addVpnUserLocked(user.id);
3636bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                }
3646bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
3656bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
3666bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
3676bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3686bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
3696bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
3706bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        networkInfo.setIsAvailable(false);
3716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
3726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (networkAgent != null) {
3736bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            networkAgent.sendNetworkInfo(networkInfo);
3746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
3756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3766bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
3776bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect(NetworkAgent networkAgent) {
3786bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
3796bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        agentDisconnect(networkInfo, networkAgent);
3806bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3814ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
3826bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect() {
3836bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkInfo.isConnected()) {
3846bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect(mNetworkInfo, mNetworkAgent);
3856bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = null;
3866bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
387fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    }
388fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
389fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    /**
390e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * Establish a VPN network and return the file descriptor of the VPN
391e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * interface. This methods returns {@code null} if the application is
392100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revoked or not prepared.
393ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
394e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
395e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @return The file descriptor of the VPN interface.
396ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
39704ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
398ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check if the caller is already prepared.
399c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        UserManager mgr = UserManager.get(mContext);
4006bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (Binder.getCallingUid() != mOwnerUID) {
4017b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
402ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
403fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Check if the service is properly declared.
404199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
405199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        intent.setClassName(mPackage, config.user);
4064ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        long token = Binder.clearCallingIdentity();
4074ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
408c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Restricted users are not allowed to create VPNs, they are tied to Owner
409c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserInfo user = mgr.getUserInfo(mUserId);
410f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds            if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
411c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                throw new SecurityException("Restricted users cannot establish VPNs");
412c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
413c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
4144ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
4154ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                                                                        null, 0, mUserId);
4164ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (info == null) {
4174ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
4184ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
4194ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
4204ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
4214ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
4224ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } catch (RemoteException e) {
4234ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
4244ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
4254ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
426199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
427fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
4284c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        // Save the old config in case we need to go back.
4294c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        VpnConfig oldConfig = mConfig;
4304c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        String oldInterface = mInterface;
4314c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        Connection oldConnection = mConnection;
4326bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        NetworkAgent oldNetworkAgent = mNetworkAgent;
4336bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent = null;
4346bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        List<UidRange> oldUsers = mVpnUsers;
4354c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
436e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        // Configure the interface. Abort if any of these steps fails.
43797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
438ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
439899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            updateState(DetailedState.CONNECTING, "establish");
440c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            String interfaze = jniGetName(tun.getFd());
4414ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
442c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // TEMP use the old jni calls until there is support for netd address setting
4434ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            StringBuilder builder = new StringBuilder();
4444ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            for (LinkAddress address : config.addresses) {
4454ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                builder.append(" " + address);
44697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
4474ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
4484ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new IllegalArgumentException("At least one address must be specified");
44997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
450199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            Connection connection = new Connection();
4514ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
4524ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                        new UserHandle(mUserId))) {
453199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                throw new IllegalStateException("Cannot bind " + config.user);
454199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
4554c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
456199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = connection;
457c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = interfaze;
4584ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
4594ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Fill more values.
4604ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.user = mPackage;
4614ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.interfaze = mInterface;
462c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            config.startTime = SystemClock.elapsedRealtime();
463c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mConfig = config;
4644c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
4654ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Set up forwarding and DNS rules.
4666bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mVpnUsers = new ArrayList<UidRange>();
4676bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentConnect();
4684ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
4694c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldConnection != null) {
4704c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                mContext.unbindService(oldConnection);
4714c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
4726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // Remove the old tun's user forwarding rules
4736bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // The new tun's user rules have already been added so they will take over
4746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // as rules are deleted. This prevents data leakage as the rules are moved over.
4756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect(oldNetworkAgent);
4764c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldInterface != null && !oldInterface.equals(interfaze)) {
4774c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                jniReset(oldInterface);
4784c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
4796bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson
4806bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            try {
4816bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
4826bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            } catch (IOException e) {
4836bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                throw new IllegalStateException(
4846bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                        "Cannot set tunnel's fd as blocking=" + config.blocking, e);
4856bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            }
486ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (RuntimeException e) {
487065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey            IoUtils.closeQuietly(tun);
4886bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
4894c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            // restore old state
4904c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConfig = oldConfig;
4914c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConnection = oldConnection;
4924c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mVpnUsers = oldUsers;
4936bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = oldNetworkAgent;
4944c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mInterface = oldInterface;
495ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw e;
496ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
497199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
498ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
499899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        // TODO: ensure that contract class eventually marks as connected
500899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        updateState(DetailedState.AUTHENTICATING, "establish");
501c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        return tun;
502ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
503ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
504c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private boolean isRunningLocked() {
505c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        return mVpnUsers != null;
506c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
507c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
5086bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
509c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private void addVpnUserLocked(int user) {
510c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        if (!isRunningLocked()) {
511c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            throw new IllegalStateException("VPN is not active");
512c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
51369887e838814642a7ae78fc810656c7c8afc2a19Robert Greenwalt
514c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // add the user
5156bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mVpnUsers.add(UidRange.createForUser(user));
516c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
517c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // show the notification
518c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
519c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Load everything for the user's notification
520c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            PackageManager pm = mContext.getPackageManager();
521c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            ApplicationInfo app = null;
5226bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            final long token = Binder.clearCallingIdentity();
523c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            try {
524c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
525c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            } catch (RemoteException e) {
526c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                throw new IllegalStateException("Invalid application");
5276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            } finally {
5286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                Binder.restoreCallingIdentity(token);
529c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
530c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            String label = app.loadLabel(pm).toString();
531c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Load the icon and convert it into a bitmap.
532c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            Drawable icon = app.loadIcon(pm);
533c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            Bitmap bitmap = null;
534c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
535c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                int width = mContext.getResources().getDimensionPixelSize(
536c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                        android.R.dimen.notification_large_icon_width);
537c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                int height = mContext.getResources().getDimensionPixelSize(
538c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                        android.R.dimen.notification_large_icon_height);
539c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                icon.setBounds(0, 0, width, height);
540c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
541c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                Canvas c = new Canvas(bitmap);
542c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                icon.draw(c);
543c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                c.setBitmap(null);
544c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
545c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            showNotification(label, bitmap, user);
546c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        } else {
547c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            showNotification(null, null, user);
548c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
549c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
550c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
551c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private void removeVpnUserLocked(int user) {
552c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            if (!isRunningLocked()) {
553c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                throw new IllegalStateException("VPN is not active");
554c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
5556bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            UidRange uidRange = UidRange.createForUser(user);
5566bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            if (mNetworkAgent != null) {
5576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                mNetworkAgent.removeUidRanges(new UidRange[] { uidRange });
5586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
5596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mVpnUsers.remove(uidRange);
560c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            hideNotification(user);
561c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
562c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
563c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private void onUserAdded(int userId) {
564c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // If the user is restricted tie them to the owner's VPN
565c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        synchronized(Vpn.this) {
566c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserManager mgr = UserManager.get(mContext);
567c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserInfo user = mgr.getUserInfo(userId);
568c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            if (user.isRestricted()) {
569c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                try {
570c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    addVpnUserLocked(userId);
5716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    if (mNetworkAgent != null) {
5726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        UidRange uidRange = UidRange.createForUser(userId);
5736bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        mNetworkAgent.addUidRanges(new UidRange[] { uidRange });
5746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    }
575c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                } catch (Exception e) {
576c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    Log.wtf(TAG, "Failed to add restricted user to owner", e);
577c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
578c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
579c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
580c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
581c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
582c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private void onUserRemoved(int userId) {
583c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // clean up if restricted
584c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        synchronized(Vpn.this) {
585c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserManager mgr = UserManager.get(mContext);
586c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserInfo user = mgr.getUserInfo(userId);
587c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            if (user.isRestricted()) {
588c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                try {
589c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    removeVpnUserLocked(userId);
590c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                } catch (Exception e) {
591c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    Log.wtf(TAG, "Failed to remove restricted user to owner", e);
592c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
593c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
594c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
595c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
596c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
597bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    /**
598bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     * Return the configuration of the currently running VPN.
599bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     */
600bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    public VpnConfig getVpnConfig() {
601bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        enforceControlPermission();
602bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        return mConfig;
603bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    }
604bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker
605899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    @Deprecated
606899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    public synchronized void interfaceStatusChanged(String iface, boolean up) {
607899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
608899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mObserver.interfaceStatusChanged(iface, up);
609899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
610899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // ignored; target is local
611aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
612ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
613ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
614899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
615899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
616899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceStatusChanged(String interfaze, boolean up) {
617899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
618899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!up && mLegacyVpnRunner != null) {
619899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mLegacyVpnRunner.check(interfaze);
620899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
621199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
622ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
623ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
624899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
625899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceRemoved(String interfaze) {
626899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
627899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
6286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    for (UidRange uidRange : mVpnUsers) {
6296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        hideNotification(uidRange.getStartUser());
630899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
6316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mVpnUsers = null;
632899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mInterface = null;
633899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    if (mConnection != null) {
634899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mContext.unbindService(mConnection);
635899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mConnection = null;
6366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        agentDisconnect();
637899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    } else if (mLegacyVpnRunner != null) {
638899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner.exit();
639899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner = null;
640899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
641899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
642899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
643899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
644899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    };
645db3c8678e5cbdfec011afaf25bde2091152c30adHaoyu Bai
646dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    private void enforceControlPermission() {
647dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // System user is allowed to control VPN.
648dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        if (Binder.getCallingUid() == Process.SYSTEM_UID) {
649dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            return;
650dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        }
6514ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        int appId = UserHandle.getAppId(Binder.getCallingUid());
6524ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        final long token = Binder.clearCallingIdentity();
653dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        try {
654212a195f008688c5483bf369d5a96753d582a24bNick Kralevich            // System VPN dialogs are also allowed to control VPN.
655dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            PackageManager pm = mContext.getPackageManager();
656dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
657212a195f008688c5483bf369d5a96753d582a24bNick Kralevich            if (((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) && (appId == app.uid)) {
658dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh                return;
659dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            }
660dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        } catch (Exception e) {
661dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            // ignore
6624ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
6634ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
664dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        }
665dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh
666dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        throw new SecurityException("Unauthorized Caller");
667dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    }
668dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh
669199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private class Connection implements ServiceConnection {
670199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        private IBinder mService;
671199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
672199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
673199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceConnected(ComponentName name, IBinder service) {
674199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = service;
675199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
676199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
677199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
678199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceDisconnected(ComponentName name) {
679199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = null;
680199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
681199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    }
682199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
683c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private void showNotification(String label, Bitmap icon, int user) {
68469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (!mEnableNotif) return;
6856bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        final long token = Binder.clearCallingIdentity();
6866bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
6876bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
6886bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
6896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            NotificationManager nm = (NotificationManager)
6906bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
6916bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
6926bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            if (nm != null) {
6936bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                String title = (label == null) ? mContext.getString(R.string.vpn_title) :
6946bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        mContext.getString(R.string.vpn_title_long, label);
6956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
6966bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        mContext.getString(R.string.vpn_text_long, mConfig.session);
6976bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
6986bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                Notification notification = new Notification.Builder(mContext)
6996bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        .setSmallIcon(R.drawable.vpn_connected)
7006bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        .setLargeIcon(icon)
7016bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        .setContentTitle(title)
7026bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        .setContentText(text)
7036bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        .setContentIntent(mStatusIntent)
7046bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        .setDefaults(0)
7056bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        .setOngoing(true)
7066bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        .build();
7076bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
7086bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
7096bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } finally {
7106bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Binder.restoreCallingIdentity(token);
711ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
712ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
713ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
714c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private void hideNotification(int user) {
71569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (!mEnableNotif) return;
716899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        mStatusIntent = null;
717899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
718ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        NotificationManager nm = (NotificationManager)
719ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
720ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
721ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (nm != null) {
7226bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            final long token = Binder.clearCallingIdentity();
7236bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            try {
7246bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
7256bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            } finally {
7266bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                Binder.restoreCallingIdentity(token);
7276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
728ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
729ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
730ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
73197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniCreate(int mtu);
732c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native String jniGetName(int tun);
73397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniSetAddresses(String interfaze, String addresses);
734c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniReset(String interfaze);
735c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native int jniCheck(String interfaze);
73685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
73741fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti    private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
73841fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        for (RouteInfo route : prop.getAllRoutes()) {
73982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            // Currently legacy VPN only works on IPv4.
74082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
74141fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti                return route;
74282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            }
74382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
74482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
74541fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        throw new IllegalStateException("Unable to find IPv4 default gateway");
74682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
74782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
74885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
74982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * Start legacy VPN, controlling native daemons as needed. Creates a
75082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * secondary thread to perform connection work, returning quickly.
75185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
75282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
7535a6bdc46e2fdc8cfd930396773dd89efd19fa1f1Robert Greenwalt        enforceControlPermission();
754b9594ce9ebb3f5f303a280f04312ae5754ce3560Kenny Root        if (!keyStore.isUnlocked()) {
75582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            throw new IllegalStateException("KeyStore isn't unlocked");
75682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
757f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        UserManager mgr = UserManager.get(mContext);
758f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        UserInfo user = mgr.getUserInfo(mUserId);
759f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
760f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds            throw new SecurityException("Restricted users cannot establish VPNs");
761f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        }
76282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
76341fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
76441fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
76541fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String iface = ipv4DefaultRoute.getInterface();
76682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
76782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Load certificates.
76882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String privateKey = "";
76982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String userCert = "";
77082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String caCert = "";
77182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String serverCert = "";
77282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecUserCert.isEmpty()) {
77382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
77482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
775d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
77682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
77782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecCaCert.isEmpty()) {
77882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
779d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
78082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
78182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecServerCert.isEmpty()) {
78282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
783d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
78482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
78582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
78682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            throw new IllegalStateException("Cannot load credentials");
78782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
78882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
78982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for racoon.
79082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] racoon = null;
79182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
79282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
79382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
79482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
79582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, "1701",
79682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
79782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
79882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
79982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
80082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udprsa", privateKey, userCert,
80182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, "1701",
80282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
80382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
80482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
80582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
80682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
80782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
80882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
80982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
81082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
81182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
81282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthrsa", privateKey, userCert,
81382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
81482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
81582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
81682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
81782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
81882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "hybridrsa",
81982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
82082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
82182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
82282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
82382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
82482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for mtpd.
82582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] mtpd = null;
82682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
82782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_PPTP:
82882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
82982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "pptp", profile.server, "1723",
83082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
83182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
83282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
83382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    (profile.mppe ? "+mppe" : "nomppe"),
83482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
83582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
83682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
83782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
83882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
83982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
84082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
84182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
84282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
84382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
84482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
84582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
846899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
84782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        VpnConfig config = new VpnConfig();
848899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        config.legacy = true;
84982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.user = profile.key;
85082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.interfaze = iface;
85182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.session = profile.name;
8524ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
8534ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        config.addLegacyRoutes(profile.routes);
85482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.dnsServers.isEmpty()) {
85582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
85682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
85782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.searchDomains.isEmpty()) {
85882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
85982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
86082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        startLegacyVpn(config, racoon, mtpd);
86182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
86282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
86382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
86482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        stopLegacyVpn();
865899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
866100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Prepare for the new request. This also checks the caller.
867100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        prepare(null, VpnConfig.LEGACY_VPN);
868899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        updateState(DetailedState.CONNECTING, "startLegacyVpn");
86985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
8702e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Start a new LegacyVpnRunner and we are done!
871100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
872100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner.start();
87385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
87485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
875899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    public synchronized void stopLegacyVpn() {
876899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner != null) {
877899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner.exit();
878899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner = null;
879899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
880899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (LegacyVpnRunner.TAG) {
881899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // wait for old thread to completely finish before spinning up
882899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // new instance, otherwise state updates can be out of order.
883899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
884899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
885899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
886899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
88785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
8882e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Return the information of the current ongoing legacy VPN.
8892e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     */
8902e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
891dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // Check if the caller is authorized.
892dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
893899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner == null) return null;
894899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
895899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        final LegacyVpnInfo info = new LegacyVpnInfo();
896c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        info.key = mConfig.user;
897899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
898899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mNetworkInfo.isConnected()) {
899899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            info.intent = mStatusIntent;
900899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
901899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        return info;
9022e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    }
9032e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
90469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public VpnConfig getLegacyVpnConfig() {
90569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (mLegacyVpnRunner != null) {
906c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            return mConfig;
90769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else {
90869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            return null;
90969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
91069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
91169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
9122e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    /**
91385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Bringing up a VPN connection takes time, and that is all this thread
91485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * does. Here we have plenty of time. The only thing we need to take
91585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * care of is responding to interruptions as soon as possible. Otherwise
91685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * requests will be piled up. This can be done in a Handler as a state
91785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * machine, but it is much easier to read in the current form.
91885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
91985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private class LegacyVpnRunner extends Thread {
92085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String TAG = "LegacyVpnRunner";
92185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
9221f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh        private final String[] mDaemons;
92385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[][] mArguments;
9245317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh        private final LocalSocket[] mSockets;
92553c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt        private final String mOuterInterface;
9261b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final AtomicInteger mOuterConnection =
9271b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                new AtomicInteger(ConnectivityManager.TYPE_NONE);
9282e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
92985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private long mTimer = -1;
93085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
9311b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        /**
9321b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         * Watch for the outer connection (passing in the constructor) going away.
9331b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         */
9341b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
9351b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            @Override
9361b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            public void onReceive(Context context, Intent intent) {
93757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey                if (!mEnableTeardown) return;
93857666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
9391b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
9401b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
9411b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
9421b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        NetworkInfo info = (NetworkInfo)intent.getExtra(
9431b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                ConnectivityManager.EXTRA_NETWORK_INFO);
9441b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        if (info != null && !info.isConnectedOrConnecting()) {
9451b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            try {
9461b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                mObserver.interfaceStatusChanged(mOuterInterface, false);
9471b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            } catch (RemoteException e) {}
9481b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        }
9491b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    }
9501b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                }
9511b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
9521b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        };
9531b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
95441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
95585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            super(TAG);
95641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mConfig = config;
95741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mDaemons = new String[] {"racoon", "mtpd"};
958899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // TODO: clear arguments from memory once launched
95941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mArguments = new String[][] {racoon, mtpd};
9605317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            mSockets = new LocalSocket[mDaemons.length];
96153c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt
96253c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // This is the interface which VPN is running on,
96353c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // mConfig.interfaze will change to point to OUR
96453c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // internal interface soon. TODO - add inner/outer to mconfig
9651b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
9664ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // we will leave the VPN up.  We should check that it's still there/connected after
9671b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // registering
96853c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            mOuterInterface = mConfig.interfaze;
9691b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
9701b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            try {
9711b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mOuterConnection.set(
9721b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        mConnService.findConnectionTypeForIface(mOuterInterface));
9731b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            } catch (Exception e) {
9741b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mOuterConnection.set(ConnectivityManager.TYPE_NONE);
9751b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
9761b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
9771b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            IntentFilter filter = new IntentFilter();
9781b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
9791b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            mContext.registerReceiver(mBroadcastReceiver, filter);
98041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
98141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
982aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        public void check(String interfaze) {
98353c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            if (interfaze.equals(mOuterInterface)) {
984aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
985aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                exit();
986aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            }
987aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
988aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh
98941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public void exit() {
9905317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            // We assume that everything is reset after stopping the daemons.
99197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            interrupt();
9925317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            for (LocalSocket socket : mSockets) {
993065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey                IoUtils.closeQuietly(socket);
99441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            }
9956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
9961b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            try {
9971b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mContext.unregisterReceiver(mBroadcastReceiver);
9981b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            } catch (IllegalArgumentException e) {}
9992e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
10002e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
100185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        @Override
100285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public void run() {
100385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Wait for the previous thread since it has been interrupted.
10042e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            Log.v(TAG, "Waiting");
100585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            synchronized (TAG) {
10062e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.v(TAG, "Executing");
100785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                execute();
1008899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                monitorDaemons();
100985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
101085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
101185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
101285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void checkpoint(boolean yield) throws InterruptedException {
101385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            long now = SystemClock.elapsedRealtime();
101485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            if (mTimer == -1) {
101585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                mTimer = now;
101685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(1);
10177ef8611b5f3a893a46c7b9e22bdd8ab252e373ffChia-chi Yeh            } else if (now - mTimer <= 60000) {
101885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(yield ? 200 : 1);
101985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else {
1020899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.FAILED, "checkpoint");
102197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                throw new IllegalStateException("Time is up");
102285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
102385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
102485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
102585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void execute() {
102685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Catch all exceptions so we can clean up few things.
1027899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            boolean initFinished = false;
102885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            try {
102985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Initialize the timer.
103085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                checkpoint(false);
103185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10321f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Wait for the daemons to stop.
10331f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (String daemon : mDaemons) {
1034088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isStopped(daemon)) {
103585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
103685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
103785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
103885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
103997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Clear the previous state.
104097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                File state = new File("/data/misc/vpn/state");
104197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                state.delete();
104297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (state.exists()) {
104397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot delete the state");
104485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1045c1872732922214de80f790e14865e41dd1b98203Chia-chi Yeh                new File("/data/misc/vpn/abort").delete();
1046899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                initFinished = true;
104785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
1048e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // Check if we need to restart any of the daemons.
104985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                boolean restart = false;
105085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String[] arguments : mArguments) {
105185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    restart = restart || (arguments != null);
105285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
105385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                if (!restart) {
10546bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentDisconnect();
105585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    return;
105685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1057899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.CONNECTING, "execute");
105885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10591f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Start the daemon with arguments.
10601f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (int i = 0; i < mDaemons.length; ++i) {
106185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String[] arguments = mArguments[i];
106285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    if (arguments == null) {
106385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        continue;
106485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
106585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10661f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Start the daemon.
10671f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String daemon = mDaemons[i];
1068088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    SystemService.start(daemon);
106985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10701f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Wait for the daemon to start.
1071088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isRunning(daemon)) {
107285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
107385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
107485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
107585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Create the control socket.
10765317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i] = new LocalSocket();
107785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocketAddress address = new LocalSocketAddress(
10781f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                            daemon, LocalSocketAddress.Namespace.RESERVED);
107985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
108085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the socket to connect.
108185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (true) {
108285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        try {
10835317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                            mSockets[i].connect(address);
108485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            break;
108585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        } catch (Exception e) {
108685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            // ignore
108785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
108885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
108985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
10905317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i].setSoTimeout(500);
109185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
109285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send over the arguments.
10935317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    OutputStream out = mSockets[i].getOutputStream();
109485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (String argument : arguments) {
1095d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                        byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
10965317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                        if (bytes.length >= 0xFFFF) {
109797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            throw new IllegalArgumentException("Argument is too large");
109885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
10991f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length >> 8);
11001f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length);
11011f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes);
110285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(false);
110385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
11045317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
11055317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
11061f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.flush();
110797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
110897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    // Wait for End-of-File.
11095317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    InputStream in = mSockets[i].getInputStream();
111097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    while (true) {
111197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        try {
111297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            if (in.read() == -1) {
111397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                                break;
111497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            }
111597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        } catch (Exception e) {
111697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            // ignore
111797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        }
111897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        checkpoint(true);
111997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
112085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
112185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
112297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Wait for the daemons to create the new state.
112397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                while (!state.exists()) {
11241f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Check if a running daemon is dead.
11251f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    for (int i = 0; i < mDaemons.length; ++i) {
11261f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        String daemon = mDaemons[i];
1127088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                        if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
11282e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                            throw new IllegalStateException(daemon + " is dead");
112985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
113085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
113185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
113285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
113385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
113497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Now we are connected. Read and parse the new state.
1135c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yeh                String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
113697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (parameters.length != 6) {
113797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot parse the state");
113897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
113997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
114097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the interface and the addresses in the config.
114197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                mConfig.interfaze = parameters[0].trim();
114285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
11434ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                mConfig.addLegacyAddresses(parameters[1]);
114497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the routes if they are not set in the config.
114597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
11464ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    mConfig.addLegacyRoutes(parameters[2]);
114797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
114897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
114997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the DNS servers if they are not set in the config.
115041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
115197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String dnsServers = parameters[3].trim();
115241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    if (!dnsServers.isEmpty()) {
115341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
115441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
115541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
115641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
115797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the search domains if they are not set in the config.
115897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
115997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String searchDomains = parameters[4].trim();
116097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    if (!searchDomains.isEmpty()) {
116197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
116297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
116397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
116497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
116597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Here is the last step and it must be done synchronously.
116641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                synchronized (Vpn.this) {
11672b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    // Set the start time
11682b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    mConfig.startTime = SystemClock.elapsedRealtime();
11692b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde
117041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    // Check if the thread is interrupted while we are waiting.
117141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    checkpoint(false);
117241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1173e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Check if the interface is gone while we are waiting.
1174c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    if (jniCheck(mConfig.interfaze) == 0) {
117534e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                        throw new IllegalStateException(mConfig.interfaze + " is gone");
117641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
1177e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
1178e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Now INetworkManagementEventObserver is watching our back.
1179c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    mInterface = mConfig.interfaze;
11806bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mVpnUsers = new ArrayList<UidRange>();
11816bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
11826bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentConnect();
11832e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
11842e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    Log.i(TAG, "Connected!");
118541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
118685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } catch (Exception e) {
11872e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.i(TAG, "Aborting", e);
1188e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                exit();
11892e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            } finally {
11905317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                // Kill the daemons if they fail to stop.
1191899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!initFinished) {
11925317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    for (String daemon : mDaemons) {
1193088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                        SystemService.stop(daemon);
11945317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    }
11955317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                }
11965317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh
11972e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                // Do not leave an unstable state.
1198899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
11996bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentDisconnect();
12002e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                }
120185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
120285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
1203899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1204899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        /**
1205899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * Monitor the daemons we started, moving to disconnected state if the
1206899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * underlying services fail.
1207899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         */
1208899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        private void monitorDaemons() {
1209899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            if (!mNetworkInfo.isConnected()) {
1210899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                return;
1211899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1212899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1213899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            try {
1214899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                while (true) {
1215899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    Thread.sleep(2000);
1216899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    for (int i = 0; i < mDaemons.length; i++) {
1217899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1218899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                            return;
1219899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        }
1220899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
1221899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1222899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            } catch (InterruptedException e) {
1223899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
1224899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            } finally {
1225899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                for (String daemon : mDaemons) {
1226899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    SystemService.stop(daemon);
1227899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1228899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
12296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                agentDisconnect();
1230899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1231899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
123285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
1233ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh}
1234