Vpn.java revision 5026279ce45ae78126046607a2634dc9dae93199
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;
200784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport static android.os.UserHandle.PER_USER_RANGE;
215026279ce45ae78126046607a2634dc9dae93199Lorenzo Colittiimport static android.net.RouteInfo.RTN_THROW;
2242065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandranimport static android.system.OsConstants.AF_INET;
2342065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandranimport static android.system.OsConstants.AF_INET6;
24899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
254ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.app.AppGlobals;
2605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidsonimport android.app.AppOpsManager;
2790b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidsonimport android.app.PendingIntent;
281b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.content.BroadcastReceiver;
29199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ComponentName;
30ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Context;
31ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Intent;
321b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.content.IntentFilter;
33199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ServiceConnection;
34ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.ApplicationInfo;
35ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.PackageManager;
366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.content.pm.PackageManager.NameNotFoundException;
37199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.pm.ResolveInfo;
38c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.content.pm.UserInfo;
39899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.ConnectivityManager;
401b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.net.IConnectivityManager;
41ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.net.INetworkManagementEventObserver;
425026279ce45ae78126046607a2634dc9dae93199Lorenzo Colittiimport android.net.IpPrefix;
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;
5282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.RouteInfo;
536bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.UidRange;
54ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.Binder;
55c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yehimport android.os.FileUtils;
56199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.IBinder;
57899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.INetworkManagementService;
586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.os.Looper;
59199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.Parcel;
60ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.ParcelFileDescriptor;
6185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.Process;
62899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.RemoteException;
6385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemClock;
64088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkeyimport android.os.SystemService;
6550cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackbornimport android.os.UserHandle;
66c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.os.UserManager;
6782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.Credentials;
6882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.KeyStore;
69ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.util.Log;
70ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
71c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport com.android.internal.annotations.GuardedBy;
722e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
7304ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig;
7482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport com.android.internal.net.VpnProfile;
75899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport com.android.server.net.BaseNetworkObserver;
76ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
7705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidsonimport libcore.io.IoUtils;
7805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
7997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.File;
806bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidsonimport java.io.IOException;
8197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.InputStream;
8285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream;
8382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport java.net.Inet4Address;
84f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandranimport java.net.Inet6Address;
85f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandranimport java.net.InetAddress;
86d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets;
876bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.ArrayList;
8841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yehimport java.util.Arrays;
896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.List;
900784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport java.util.SortedSet;
910784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport java.util.TreeSet;
921b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport java.util.concurrent.atomic.AtomicInteger;
9385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
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;
11042065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran    private boolean mAllowIPv4;
11142065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran    private boolean mAllowIPv6;
112199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private Connection mConnection;
11385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private LegacyVpnRunner mLegacyVpnRunner;
11490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    private PendingIntent mStatusIntent;
11557666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    private volatile boolean mEnableTeardown = true;
1161b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt    private final IConnectivityManager mConnService;
1176bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final INetworkManagementService mNetd;
118c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private VpnConfig mConfig;
1196bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkAgent mNetworkAgent;
1206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final Looper mLooper;
1216bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final NetworkCapabilities mNetworkCapabilities;
122c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
123c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    /* list of users using this VPN. */
124c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    @GuardedBy("this")
1256bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private List<UidRange> mVpnUsers = null;
126c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private BroadcastReceiver mUserIntentReceiver = null;
127c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
1280784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Handle of user initiating VPN.
1290784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private final int mUserHandle;
130ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
1316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public Vpn(Looper looper, Context context, INetworkManagementService netService,
1320784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            IConnectivityManager connService, int userHandle) {
133ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext = context;
1346bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetd = netService;
1351b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        mConnService = connService;
1360784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mUserHandle = userHandle;
1376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mLooper = looper;
1386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
1396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mPackage = VpnConfig.LEGACY_VPN;
1400784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mOwnerUID = getAppUid(mPackage, mUserHandle);
141899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
142899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
143899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            netService.registerObserver(mObserver);
144899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
145899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            Log.wtf(TAG, "Problem registering observer", e);
146899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
1470784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        if (userHandle == UserHandle.USER_OWNER) {
148c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Owner's VPN also needs to handle restricted users
149c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mUserIntentReceiver = new BroadcastReceiver() {
150c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                @Override
151c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                public void onReceive(Context context, Intent intent) {
152c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    final String action = intent.getAction();
1530784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
154c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                            UserHandle.USER_NULL);
1550784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    if (userHandle == UserHandle.USER_NULL) return;
156c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
157c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    if (Intent.ACTION_USER_ADDED.equals(action)) {
1580784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        onUserAdded(userHandle);
159c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
1600784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        onUserRemoved(userHandle);
161c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    }
162c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
163c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            };
164c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
165c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            IntentFilter intentFilter = new IntentFilter();
166c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            intentFilter.addAction(Intent.ACTION_USER_ADDED);
167c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            intentFilter.addAction(Intent.ACTION_USER_REMOVED);
168c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mContext.registerReceiverAsUser(
169c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
170c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
1716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
1726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
1736bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
1746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities = new NetworkCapabilities();
1756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
1766bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
177899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
178899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
17957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    /**
18057666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * Set if this object is responsible for watching for {@link NetworkInfo}
18157666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * teardown. When {@code false}, teardown is handled externally by someone
18257666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * else.
18357666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     */
18457666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    public void setEnableTeardown(boolean enableTeardown) {
18557666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mEnableTeardown = enableTeardown;
18657666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    }
18757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
188899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    /**
189899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     * Update current state, dispaching event to listeners.
190899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     */
191899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private void updateState(DetailedState detailedState, String reason) {
192899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
193899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        mNetworkInfo.setDetailedState(detailedState, reason, null);
1946bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkAgent != null) {
1956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
1966bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
197ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
198ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
199ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
200100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Prepare for a VPN application. This method is designed to solve
201100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * race conditions. It first compares the current prepared package
202100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * with {@code oldPackage}. If they are the same, the prepared
203100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * package is revoked and replaced with {@code newPackage}. If
204100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * {@code oldPackage} is {@code null}, the comparison is omitted.
205100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * If {@code newPackage} is the same package or {@code null}, the
206100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revocation is omitted. This method returns {@code true} if the
207100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * operation is succeeded.
208e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     *
209100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Legacy VPN is handled specially since it is not a real package.
210100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
211100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * it can be revoked by itself.
212100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     *
213100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param oldPackage The package name of the old VPN application.
214100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param newPackage The package name of the new VPN application.
215100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @return true if the operation is succeeded.
216ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
217100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh    public synchronized boolean prepare(String oldPackage, String newPackage) {
218100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return false if the package does not match.
219c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (oldPackage != null && !oldPackage.equals(mPackage)) {
22005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            // The package doesn't match. If this VPN was not previously authorized, return false
22105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            // to force user authorization. Otherwise, revoke the VPN anyway.
22205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
22305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                long token = Binder.clearCallingIdentity();
22405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                try {
22505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    // This looks bizarre, but it is what ConfirmDialog in VpnDialogs is doing when
22605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    // the user clicks through to allow the VPN to consent. So we are emulating the
22705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    // action of the dialog without actually showing it.
22805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    prepare(null, oldPackage);
22905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                } finally {
23005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    Binder.restoreCallingIdentity(token);
23105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                }
23205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                return true;
23305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            }
234100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return false;
235100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        }
236100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh
237100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return true if we do not need to revoke.
238100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        if (newPackage == null ||
239c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
240100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return true;
241ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
242ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
243dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // Check if the caller is authorized.
244dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
2457b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh
24605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Reset the interface.
247c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (mInterface != null) {
24890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            mStatusIntent = null;
2496bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
2504ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            jniReset(mInterface);
251c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = null;
252c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mVpnUsers = null;
253ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
254ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
255fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Revoke the connection or stop LegacyVpnRunner.
256199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        if (mConnection != null) {
257199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            try {
258199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
259199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                        Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
260199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            } catch (Exception e) {
261199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                // ignore
262199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
263199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mContext.unbindService(mConnection);
264199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = null;
265e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        } else if (mLegacyVpnRunner != null) {
26641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mLegacyVpnRunner.exit();
26741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mLegacyVpnRunner = null;
26841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
26941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
2706bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        long token = Binder.clearCallingIdentity();
2716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
2726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetd.denyProtect(mOwnerUID);
2736bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (Exception e) {
2746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
2756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } finally {
2766bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Binder.restoreCallingIdentity(token);
2776bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
2786bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
279c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
280c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        mPackage = newPackage;
2810784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mOwnerUID = getAppUid(newPackage, mUserHandle);
2826bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        token = Binder.clearCallingIdentity();
2836bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
2846bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetd.allowProtect(mOwnerUID);
2856bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (Exception e) {
2866bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
2876bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } finally {
2886bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Binder.restoreCallingIdentity(token);
2896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
290c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        mConfig = null;
29105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
292899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        updateState(DetailedState.IDLE, "prepare");
293100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        return true;
294ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
295ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
29605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    /**
29705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson     * Set whether the current package has the ability to launch VPNs without user intervention.
29805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson     */
29905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    public void setPackageAuthorization(boolean authorized) {
30005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Check if the caller is authorized.
30105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        enforceControlPermission();
30205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
30305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        if (mPackage == null || VpnConfig.LEGACY_VPN.equals(mPackage)) {
30405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            return;
30505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        }
30605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
30705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        long token = Binder.clearCallingIdentity();
30805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        try {
30905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            AppOpsManager appOps =
31005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
31105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, mOwnerUID, mPackage,
31205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
31305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        } catch (Exception e) {
31405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            Log.wtf(TAG, "Failed to set app ops for package " + mPackage, e);
31505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        } finally {
31605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            Binder.restoreCallingIdentity(token);
31705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        }
31805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    }
31905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
32005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    private boolean isVpnUserPreConsented(String packageName) {
32105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        AppOpsManager appOps =
32205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
32305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
32405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Verify that the caller matches the given package and has permission to activate VPNs.
32505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
32605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                packageName) == AppOpsManager.MODE_ALLOWED;
32705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    }
32805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
3290784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private int getAppUid(String app, int userHandle) {
33005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        if (VpnConfig.LEGACY_VPN.equals(app)) {
3316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            return Process.myUid();
3326bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
333fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
3346bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        int result;
3356bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
3360784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            result = pm.getPackageUid(app, userHandle);
3376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (NameNotFoundException e) {
3386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            result = -1;
339fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        }
3406bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return result;
3416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3426bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
3436bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public NetworkInfo getNetworkInfo() {
3446bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return mNetworkInfo;
3456bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3466bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
3476bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentConnect() {
3486bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        LinkProperties lp = new LinkProperties();
3496bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setInterfaceName(mInterface);
35042065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
3516bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        boolean hasDefaultRoute = false;
3526bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        for (RouteInfo route : mConfig.routes) {
3536bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            lp.addRoute(route);
3546bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            if (route.isDefaultRoute()) hasDefaultRoute = true;
3556bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
3566bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (hasDefaultRoute) {
3576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
3586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } else {
3596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
3606bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
36142065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
3626bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.dnsServers != null) {
3636bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String dnsServer : mConfig.dnsServers) {
36442065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                InetAddress address = InetAddress.parseNumericAddress(dnsServer);
36542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                lp.addDnsServer(address);
36642065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                if (address instanceof Inet4Address) {
36742065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                    mAllowIPv4 = true;
36842065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                } else {
36942065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                    mAllowIPv6 = true;
37042065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                }
3716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
3726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
37342065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
3746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // Concatenate search domains into a string.
3756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        StringBuilder buffer = new StringBuilder();
3766bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.searchDomains != null) {
3776bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String domain : mConfig.searchDomains) {
3786bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                buffer.append(domain).append(' ');
3796bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
3806bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
3816bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setDomains(buffer.toString().trim());
38242065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
3836bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo.setIsAvailable(true);
3846bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
38542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
3868cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran        NetworkMisc networkMisc = new NetworkMisc();
38742065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran        networkMisc.allowBypass = mConfig.allowBypass;
38842065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
3896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        long token = Binder.clearCallingIdentity();
3904ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
3916bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
3928cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
39305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                            @Override
3946bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                            public void unwanted() {
3956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                                // We are user controlled, not driven by NetworkRequest.
39605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                            }
3976bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        };
3984ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
3994ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
4004ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        }
40142065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
40242065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran        if (!mAllowIPv4) {
40342065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran            mNetworkAgent.blockAddressFamily(AF_INET);
40442065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran        }
40542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran        if (!mAllowIPv6) {
40642065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran            mNetworkAgent.blockAddressFamily(AF_INET6);
40742065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran        }
40842065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
4090784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        addVpnUserLocked(mUserHandle);
4106bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // If we are owner assign all Restricted Users to this VPN
4110784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        if (mUserHandle == UserHandle.USER_OWNER) {
4126bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            token = Binder.clearCallingIdentity();
4136bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            List<UserInfo> users;
4146bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            try {
4156bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                users = UserManager.get(mContext).getUsers();
4166bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            } finally {
4176bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                Binder.restoreCallingIdentity(token);
4186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
4196bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (UserInfo user : users) {
4206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                if (user.isRestricted()) {
4216bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    addVpnUserLocked(user.id);
4226bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                }
4236bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
4246bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
4256bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
4266bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
4276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
4286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
4296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        networkInfo.setIsAvailable(false);
4306bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
4316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (networkAgent != null) {
4326bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            networkAgent.sendNetworkInfo(networkInfo);
4336bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
4346bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
4356bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
4366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect(NetworkAgent networkAgent) {
4376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
4386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        agentDisconnect(networkInfo, networkAgent);
4396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
4404ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
4416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect() {
4426bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkInfo.isConnected()) {
4436bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect(mNetworkInfo, mNetworkAgent);
4446bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = null;
4456bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
446fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    }
447fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
448fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    /**
449e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * Establish a VPN network and return the file descriptor of the VPN
450e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * interface. This methods returns {@code null} if the application is
451100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revoked or not prepared.
452ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
453e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
454e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @return The file descriptor of the VPN interface.
455ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
45604ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
457ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check if the caller is already prepared.
458c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        UserManager mgr = UserManager.get(mContext);
4596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (Binder.getCallingUid() != mOwnerUID) {
4607b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
461ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
462fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Check if the service is properly declared.
463199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
464199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        intent.setClassName(mPackage, config.user);
4654ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        long token = Binder.clearCallingIdentity();
4664ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
467c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Restricted users are not allowed to create VPNs, they are tied to Owner
4680784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            UserInfo user = mgr.getUserInfo(mUserHandle);
469f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds            if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
470c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                throw new SecurityException("Restricted users cannot establish VPNs");
471c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
472c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
4734ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
4740784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                                                                        null, 0, mUserHandle);
4754ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (info == null) {
4764ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
4774ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
4784ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
4794ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
4804ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
4814ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } catch (RemoteException e) {
4824ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
4834ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
4844ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
485199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
486fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
4874c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        // Save the old config in case we need to go back.
4884c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        VpnConfig oldConfig = mConfig;
4894c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        String oldInterface = mInterface;
4904c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        Connection oldConnection = mConnection;
4916bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        NetworkAgent oldNetworkAgent = mNetworkAgent;
4926bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent = null;
4936bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        List<UidRange> oldUsers = mVpnUsers;
49442065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran        boolean oldAllowIPv4 = mAllowIPv4;
49542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran        boolean oldAllowIPv6 = mAllowIPv6;
4964c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
497e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        // Configure the interface. Abort if any of these steps fails.
49897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
499ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
500899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            updateState(DetailedState.CONNECTING, "establish");
501c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            String interfaze = jniGetName(tun.getFd());
5024ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
503c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // TEMP use the old jni calls until there is support for netd address setting
5044ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            StringBuilder builder = new StringBuilder();
5054ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            for (LinkAddress address : config.addresses) {
5064ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                builder.append(" " + address);
50797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
5084ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
5094ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new IllegalArgumentException("At least one address must be specified");
51097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
511199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            Connection connection = new Connection();
5124ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
5130784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        new UserHandle(mUserHandle))) {
514199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                throw new IllegalStateException("Cannot bind " + config.user);
515199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
5164c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
517199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = connection;
518c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = interfaze;
5194ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
5204ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Fill more values.
5214ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.user = mPackage;
5224ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.interfaze = mInterface;
523c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            config.startTime = SystemClock.elapsedRealtime();
524c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mConfig = config;
5254c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
5264ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Set up forwarding and DNS rules.
5276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mVpnUsers = new ArrayList<UidRange>();
52842065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran            mAllowIPv4 = mConfig.allowIPv4;
52942065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran            mAllowIPv6 = mConfig.allowIPv6;
53042065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
5316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentConnect();
5324ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
5334c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldConnection != null) {
5344c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                mContext.unbindService(oldConnection);
5354c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
5366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // Remove the old tun's user forwarding rules
5376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // The new tun's user rules have already been added so they will take over
5386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // as rules are deleted. This prevents data leakage as the rules are moved over.
5396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect(oldNetworkAgent);
5404c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldInterface != null && !oldInterface.equals(interfaze)) {
5414c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                jniReset(oldInterface);
5424c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
5436bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson
5446bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            try {
5456bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
5466bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            } catch (IOException e) {
5476bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                throw new IllegalStateException(
5486bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                        "Cannot set tunnel's fd as blocking=" + config.blocking, e);
5496bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            }
550ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (RuntimeException e) {
551065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey            IoUtils.closeQuietly(tun);
5526bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
5534c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            // restore old state
5544c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConfig = oldConfig;
5554c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConnection = oldConnection;
5564c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mVpnUsers = oldUsers;
5576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = oldNetworkAgent;
5584c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mInterface = oldInterface;
55942065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran            mAllowIPv4 = oldAllowIPv4;
56042065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran            mAllowIPv6 = oldAllowIPv6;
561ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw e;
562ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
563199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
564c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        return tun;
565ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
566ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
567c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private boolean isRunningLocked() {
568c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        return mVpnUsers != null;
569c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
570c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
5710784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Note: Return type guarantees results are deduped and sorted, which callers require.
5720784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
5730784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        SortedSet<Integer> uids = new TreeSet<Integer>();
5740784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        for (String app : packageNames) {
5750784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int uid = getAppUid(app, userHandle);
5760784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (uid != -1) uids.add(uid);
5770784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
5780784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        return uids;
5790784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    }
5800784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen
5816bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
5820784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void addVpnUserLocked(int userHandle) {
583c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        if (!isRunningLocked()) {
584c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            throw new IllegalStateException("VPN is not active");
585c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
58669887e838814642a7ae78fc810656c7c8afc2a19Robert Greenwalt
5870784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        if (mConfig.allowedApplications != null) {
5880784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add ranges covering all UIDs for allowedApplications.
5890784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int start = -1, stop = -1;
5900784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            for (int uid : getAppsUids(mConfig.allowedApplications, userHandle)) {
5910784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                if (start == -1) {
5920784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid;
5930784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                } else if (uid != stop + 1) {
5940784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    mVpnUsers.add(new UidRange(start, stop));
5950784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid;
5960784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                }
5970784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                stop = uid;
5980784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
5990784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (start != -1) mVpnUsers.add(new UidRange(start, stop));
6000784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        } else if (mConfig.disallowedApplications != null) {
6010784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add all ranges for user skipping UIDs for disallowedApplications.
6020784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            final UidRange userRange = UidRange.createForUser(userHandle);
6030784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int start = userRange.start;
6040784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            for (int uid : getAppsUids(mConfig.disallowedApplications, userHandle)) {
6050784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                if (uid == start) {
6060784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start++;
6070784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                } else {
6080784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    mVpnUsers.add(new UidRange(start, uid - 1));
6090784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid + 1;
6100784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                }
6110784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
6120784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (start <= userRange.stop) mVpnUsers.add(new UidRange(start, userRange.stop));
6130784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        } else {
6140784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add all UIDs for the user.
6150784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            mVpnUsers.add(UidRange.createForUser(userHandle));
6160784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
61790b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson
61890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        prepareStatusIntent();
619c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
620c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
6210784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
6220784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // apply to userHandle.
6230784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private List<UidRange> uidRangesForUser(int userHandle) {
6240784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final UidRange userRange = UidRange.createForUser(userHandle);
6250784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final List<UidRange> ranges = new ArrayList<UidRange>();
6260784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        for (UidRange range : mVpnUsers) {
6270784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (range.start >= userRange.start && range.stop <= userRange.stop) {
6280784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                ranges.add(range);
6290784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
6300784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
6310784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        return ranges;
6320784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    }
6330784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen
6340784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void removeVpnUserLocked(int userHandle) {
63590b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (!isRunningLocked()) {
63690b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            throw new IllegalStateException("VPN is not active");
63790b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
6380784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final List<UidRange> ranges = uidRangesForUser(userHandle);
63990b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (mNetworkAgent != null) {
6400784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
64190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
6420784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mVpnUsers.removeAll(ranges);
64390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        mStatusIntent = null;
644c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
645c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
6460784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void onUserAdded(int userHandle) {
647c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // If the user is restricted tie them to the owner's VPN
648c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        synchronized(Vpn.this) {
649c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserManager mgr = UserManager.get(mContext);
6500784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            UserInfo user = mgr.getUserInfo(userHandle);
651c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            if (user.isRestricted()) {
652c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                try {
6530784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    addVpnUserLocked(userHandle);
6546bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    if (mNetworkAgent != null) {
6550784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        final List<UidRange> ranges = uidRangesForUser(userHandle);
6560784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
6576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    }
658c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                } catch (Exception e) {
659c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    Log.wtf(TAG, "Failed to add restricted user to owner", e);
660c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
661c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
662c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
663c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
664c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
6650784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void onUserRemoved(int userHandle) {
666c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // clean up if restricted
667c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        synchronized(Vpn.this) {
668c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserManager mgr = UserManager.get(mContext);
6690784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            UserInfo user = mgr.getUserInfo(userHandle);
670c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            if (user.isRestricted()) {
671c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                try {
6720784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    removeVpnUserLocked(userHandle);
673c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                } catch (Exception e) {
674c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    Log.wtf(TAG, "Failed to remove restricted user to owner", e);
675c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
676c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
677c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
678c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
679c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
680bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    /**
681bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     * Return the configuration of the currently running VPN.
682bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     */
683bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    public VpnConfig getVpnConfig() {
684bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        enforceControlPermission();
685bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        return mConfig;
686bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    }
687bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker
688899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    @Deprecated
689899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    public synchronized void interfaceStatusChanged(String iface, boolean up) {
690899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
691899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mObserver.interfaceStatusChanged(iface, up);
692899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
693899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // ignored; target is local
694aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
695ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
696ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
697899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
698899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
699899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceStatusChanged(String interfaze, boolean up) {
700899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
701899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!up && mLegacyVpnRunner != null) {
702899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mLegacyVpnRunner.check(interfaze);
703899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
704199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
705ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
706ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
707899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
708899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceRemoved(String interfaze) {
709899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
710899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
71190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson                    mStatusIntent = null;
7126bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mVpnUsers = null;
713899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mInterface = null;
714899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    if (mConnection != null) {
715899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mContext.unbindService(mConnection);
716899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mConnection = null;
7176bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        agentDisconnect();
718899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    } else if (mLegacyVpnRunner != null) {
719899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner.exit();
720899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner = null;
721899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
722899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
723899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
724899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
725899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    };
726db3c8678e5cbdfec011afaf25bde2091152c30adHaoyu Bai
727dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    private void enforceControlPermission() {
728dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // System user is allowed to control VPN.
729dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        if (Binder.getCallingUid() == Process.SYSTEM_UID) {
730dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            return;
731dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        }
7324ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        int appId = UserHandle.getAppId(Binder.getCallingUid());
7334ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        final long token = Binder.clearCallingIdentity();
734dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        try {
735212a195f008688c5483bf369d5a96753d582a24bNick Kralevich            // System VPN dialogs are also allowed to control VPN.
736dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            PackageManager pm = mContext.getPackageManager();
737dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
738212a195f008688c5483bf369d5a96753d582a24bNick Kralevich            if (((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) && (appId == app.uid)) {
739dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh                return;
740dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            }
741e47df8466be991b187f445c8cf811891271051bfJason Monk            // SystemUI dialogs are also allowed to control VPN.
742e47df8466be991b187f445c8cf811891271051bfJason Monk            ApplicationInfo sysUiApp = pm.getApplicationInfo("com.android.systemui", 0);
743e47df8466be991b187f445c8cf811891271051bfJason Monk            if (((sysUiApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0) && (appId == sysUiApp.uid)) {
744e47df8466be991b187f445c8cf811891271051bfJason Monk                return;
745e47df8466be991b187f445c8cf811891271051bfJason Monk            }
746dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        } catch (Exception e) {
747dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh            // ignore
7484ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
7494ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
750dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        }
751dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh
752dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        throw new SecurityException("Unauthorized Caller");
753dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    }
754dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh
755199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private class Connection implements ServiceConnection {
756199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        private IBinder mService;
757199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
758199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
759199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceConnected(ComponentName name, IBinder service) {
760199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = service;
761199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
762199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
763199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
764199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceDisconnected(ComponentName name) {
765199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = null;
766199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
767199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    }
768199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
76990b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    private void prepareStatusIntent() {
77090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        final long token = Binder.clearCallingIdentity();
77190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        try {
77290b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
77390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        } finally {
77490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            Binder.restoreCallingIdentity(token);
77590b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
77690b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    }
77790b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson
778f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    public synchronized boolean addAddress(String address, int prefixLength) {
779f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
780f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            return false;
781f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
782f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        boolean success = jniAddAddress(mInterface, address, prefixLength);
783f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        if (success && (!mAllowIPv4 || !mAllowIPv6)) {
784f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            try {
785f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                InetAddress inetAddress = InetAddress.parseNumericAddress(address);
786f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                if ((inetAddress instanceof Inet4Address) && !mAllowIPv4) {
787f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                    mAllowIPv4 = true;
788f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                    mNetworkAgent.unblockAddressFamily(AF_INET);
789f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                } else if ((inetAddress instanceof Inet6Address) && !mAllowIPv6) {
790f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                    mAllowIPv6 = true;
791f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                    mNetworkAgent.unblockAddressFamily(AF_INET6);
792f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                }
793f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            } catch (IllegalArgumentException e) {
794f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran                // ignore
795f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            }
796f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
797f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // Ideally, we'd call mNetworkAgent.sendLinkProperties() here to notify ConnectivityService
798f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // that the LinkAddress has changed. But we don't do so for two reasons: (1) We don't set
799f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // LinkAddresses on the LinkProperties we establish in the first place (see agentConnect())
800f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // and (2) CS doesn't do anything with LinkAddresses anyway (see updateLinkProperties()).
801f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // TODO: Maybe fix this.
802f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        return success;
803f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    }
804f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran
805f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    public synchronized boolean removeAddress(String address, int prefixLength) {
806f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        if (Binder.getCallingUid() != mOwnerUID || mInterface == null || mNetworkAgent == null) {
807f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            return false;
808f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
809f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        boolean success = jniDelAddress(mInterface, address, prefixLength);
810f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // Ideally, we'd call mNetworkAgent.sendLinkProperties() here to notify ConnectivityService
811f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // that the LinkAddress has changed. But we don't do so for two reasons: (1) We don't set
812f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // LinkAddresses on the LinkProperties we establish in the first place (see agentConnect())
813f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // and (2) CS doesn't do anything with LinkAddresses anyway (see updateLinkProperties()).
814f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        // TODO: Maybe fix this.
815f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        return success;
816f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    }
817f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran
81897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniCreate(int mtu);
819c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native String jniGetName(int tun);
82097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniSetAddresses(String interfaze, String addresses);
821c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniReset(String interfaze);
822c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native int jniCheck(String interfaze);
823f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
824f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
82585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
82641fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti    private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
82741fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        for (RouteInfo route : prop.getAllRoutes()) {
82882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            // Currently legacy VPN only works on IPv4.
82982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
83041fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti                return route;
83182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            }
83282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
83382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
83441fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        throw new IllegalStateException("Unable to find IPv4 default gateway");
83582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
83682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
83785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
83882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * Start legacy VPN, controlling native daemons as needed. Creates a
83982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * secondary thread to perform connection work, returning quickly.
84085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
84182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
8425a6bdc46e2fdc8cfd930396773dd89efd19fa1f1Robert Greenwalt        enforceControlPermission();
843b9594ce9ebb3f5f303a280f04312ae5754ce3560Kenny Root        if (!keyStore.isUnlocked()) {
84482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            throw new IllegalStateException("KeyStore isn't unlocked");
84582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
846f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        UserManager mgr = UserManager.get(mContext);
8470784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        UserInfo user = mgr.getUserInfo(mUserHandle);
848f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
849f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds            throw new SecurityException("Restricted users cannot establish VPNs");
850f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        }
85182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
85241fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
85341fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
85441fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String iface = ipv4DefaultRoute.getInterface();
85582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
85682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Load certificates.
85782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String privateKey = "";
85882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String userCert = "";
85982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String caCert = "";
86082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String serverCert = "";
86182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecUserCert.isEmpty()) {
86282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
86382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
864d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
86582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
86682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecCaCert.isEmpty()) {
86782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
868d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
86982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
87082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecServerCert.isEmpty()) {
87182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
872d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
87382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
87482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
87582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            throw new IllegalStateException("Cannot load credentials");
87682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
87782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
87882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for racoon.
87982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] racoon = null;
88082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
88182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
88282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
88382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
88482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, "1701",
88582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
88682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
88782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
88882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
88982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udprsa", privateKey, userCert,
89082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, "1701",
89182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
89282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
89382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
89482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
89582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
89682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
89782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
89882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
89982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
90082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
90182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthrsa", privateKey, userCert,
90282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
90382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
90482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
90582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
90682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
90782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "hybridrsa",
90882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
90982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
91082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
91182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
91282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
91382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for mtpd.
91482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] mtpd = null;
91582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
91682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_PPTP:
91782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
91882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "pptp", profile.server, "1723",
91982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
92082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
92182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
92282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    (profile.mppe ? "+mppe" : "nomppe"),
92382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
92482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
92582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
92682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
92782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
92882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
92982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
93082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
93182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
93282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
93382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
93482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
935899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
93682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        VpnConfig config = new VpnConfig();
937899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        config.legacy = true;
93882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.user = profile.key;
93982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.interfaze = iface;
94082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.session = profile.name;
9414ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
9424ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        config.addLegacyRoutes(profile.routes);
94382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.dnsServers.isEmpty()) {
94482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
94582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
94682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.searchDomains.isEmpty()) {
94782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
94882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
94982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        startLegacyVpn(config, racoon, mtpd);
95082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
95182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
95282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
95382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        stopLegacyVpn();
954899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
955100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Prepare for the new request. This also checks the caller.
956100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        prepare(null, VpnConfig.LEGACY_VPN);
957899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        updateState(DetailedState.CONNECTING, "startLegacyVpn");
95885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
9592e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Start a new LegacyVpnRunner and we are done!
960100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
961100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner.start();
96285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
96385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
964899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    public synchronized void stopLegacyVpn() {
965899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner != null) {
966899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner.exit();
967899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner = null;
968899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
969899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (LegacyVpnRunner.TAG) {
970899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // wait for old thread to completely finish before spinning up
971899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // new instance, otherwise state updates can be out of order.
972899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
973899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
974899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
975899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
97685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
9772e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Return the information of the current ongoing legacy VPN.
9782e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     */
9792e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
980dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // Check if the caller is authorized.
981dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
982899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner == null) return null;
983899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
984899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        final LegacyVpnInfo info = new LegacyVpnInfo();
985c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        info.key = mConfig.user;
986899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
98790b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (mNetworkInfo.isConnected()) {
98890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            info.intent = mStatusIntent;
98990b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
990899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        return info;
9912e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    }
9922e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
99369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public VpnConfig getLegacyVpnConfig() {
99469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (mLegacyVpnRunner != null) {
995c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            return mConfig;
99669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else {
99769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            return null;
99869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
99969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
100069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
10012e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    /**
100285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Bringing up a VPN connection takes time, and that is all this thread
100385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * does. Here we have plenty of time. The only thing we need to take
100485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * care of is responding to interruptions as soon as possible. Otherwise
100585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * requests will be piled up. This can be done in a Handler as a state
100685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * machine, but it is much easier to read in the current form.
100785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
100885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private class LegacyVpnRunner extends Thread {
100985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String TAG = "LegacyVpnRunner";
101085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10111f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh        private final String[] mDaemons;
101285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[][] mArguments;
10135317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh        private final LocalSocket[] mSockets;
101453c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt        private final String mOuterInterface;
10151b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final AtomicInteger mOuterConnection =
10161b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                new AtomicInteger(ConnectivityManager.TYPE_NONE);
10172e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
101885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private long mTimer = -1;
101985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10201b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        /**
10211b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         * Watch for the outer connection (passing in the constructor) going away.
10221b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         */
10231b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
10241b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            @Override
10251b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            public void onReceive(Context context, Intent intent) {
102657666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey                if (!mEnableTeardown) return;
102757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
10281b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
10291b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
10301b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
10311b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        NetworkInfo info = (NetworkInfo)intent.getExtra(
10321b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                ConnectivityManager.EXTRA_NETWORK_INFO);
10331b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        if (info != null && !info.isConnectedOrConnecting()) {
10341b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            try {
10351b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                mObserver.interfaceStatusChanged(mOuterInterface, false);
10361b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            } catch (RemoteException e) {}
10371b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        }
10381b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    }
10391b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                }
10401b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
10411b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        };
10421b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
104341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
104485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            super(TAG);
104541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mConfig = config;
104641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mDaemons = new String[] {"racoon", "mtpd"};
1047899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // TODO: clear arguments from memory once launched
104841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mArguments = new String[][] {racoon, mtpd};
10495317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            mSockets = new LocalSocket[mDaemons.length];
105053c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt
105153c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // This is the interface which VPN is running on,
105253c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // mConfig.interfaze will change to point to OUR
105353c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // internal interface soon. TODO - add inner/outer to mconfig
10541b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
10554ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // we will leave the VPN up.  We should check that it's still there/connected after
10561b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // registering
105753c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            mOuterInterface = mConfig.interfaze;
10581b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
10591b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            try {
10601b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mOuterConnection.set(
10611b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        mConnService.findConnectionTypeForIface(mOuterInterface));
10621b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            } catch (Exception e) {
10631b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mOuterConnection.set(ConnectivityManager.TYPE_NONE);
10641b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
10651b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
10661b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            IntentFilter filter = new IntentFilter();
10671b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
10681b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            mContext.registerReceiver(mBroadcastReceiver, filter);
106941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
107041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1071aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        public void check(String interfaze) {
107253c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            if (interfaze.equals(mOuterInterface)) {
1073aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
1074aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                exit();
1075aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            }
1076aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
1077aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh
107841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public void exit() {
10795317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            // We assume that everything is reset after stopping the daemons.
108097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            interrupt();
10815317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            for (LocalSocket socket : mSockets) {
1082065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey                IoUtils.closeQuietly(socket);
108341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            }
10846bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
10851b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            try {
10861b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mContext.unregisterReceiver(mBroadcastReceiver);
10871b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            } catch (IllegalArgumentException e) {}
10882e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
10892e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
109085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        @Override
109185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public void run() {
109285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Wait for the previous thread since it has been interrupted.
10932e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            Log.v(TAG, "Waiting");
109485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            synchronized (TAG) {
10952e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.v(TAG, "Executing");
109685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                execute();
1097899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                monitorDaemons();
109885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
109985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
110085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
110185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void checkpoint(boolean yield) throws InterruptedException {
110285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            long now = SystemClock.elapsedRealtime();
110385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            if (mTimer == -1) {
110485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                mTimer = now;
110585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(1);
11067ef8611b5f3a893a46c7b9e22bdd8ab252e373ffChia-chi Yeh            } else if (now - mTimer <= 60000) {
110785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(yield ? 200 : 1);
110885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else {
1109899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.FAILED, "checkpoint");
111097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                throw new IllegalStateException("Time is up");
111185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
111285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
111385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
111485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void execute() {
111585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Catch all exceptions so we can clean up few things.
1116899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            boolean initFinished = false;
111785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            try {
111885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Initialize the timer.
111985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                checkpoint(false);
112085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
11211f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Wait for the daemons to stop.
11221f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (String daemon : mDaemons) {
1123088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isStopped(daemon)) {
112485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
112585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
112685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
112785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
112897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Clear the previous state.
112997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                File state = new File("/data/misc/vpn/state");
113097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                state.delete();
113197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (state.exists()) {
113297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot delete the state");
113385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1134c1872732922214de80f790e14865e41dd1b98203Chia-chi Yeh                new File("/data/misc/vpn/abort").delete();
1135899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                initFinished = true;
113685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
1137e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // Check if we need to restart any of the daemons.
113885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                boolean restart = false;
113985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String[] arguments : mArguments) {
114085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    restart = restart || (arguments != null);
114185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
114285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                if (!restart) {
11436bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentDisconnect();
114485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    return;
114585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1146899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.CONNECTING, "execute");
114785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
11481f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Start the daemon with arguments.
11491f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (int i = 0; i < mDaemons.length; ++i) {
115085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String[] arguments = mArguments[i];
115185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    if (arguments == null) {
115285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        continue;
115385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
115485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
11551f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Start the daemon.
11561f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String daemon = mDaemons[i];
1157088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    SystemService.start(daemon);
115885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
11591f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Wait for the daemon to start.
1160088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isRunning(daemon)) {
116185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
116285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
116385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
116485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Create the control socket.
11655317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i] = new LocalSocket();
116685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocketAddress address = new LocalSocketAddress(
11671f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                            daemon, LocalSocketAddress.Namespace.RESERVED);
116885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
116985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the socket to connect.
117085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (true) {
117185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        try {
11725317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                            mSockets[i].connect(address);
117385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            break;
117485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        } catch (Exception e) {
117585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            // ignore
117685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
117785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
117885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
11795317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i].setSoTimeout(500);
118085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
118185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send over the arguments.
11825317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    OutputStream out = mSockets[i].getOutputStream();
118385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (String argument : arguments) {
1184d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                        byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
11855317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                        if (bytes.length >= 0xFFFF) {
118697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            throw new IllegalArgumentException("Argument is too large");
118785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
11881f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length >> 8);
11891f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length);
11901f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes);
119185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(false);
119285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
11935317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
11945317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
11951f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.flush();
119697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
119797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    // Wait for End-of-File.
11985317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    InputStream in = mSockets[i].getInputStream();
119997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    while (true) {
120097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        try {
120197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            if (in.read() == -1) {
120297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                                break;
120397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            }
120497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        } catch (Exception e) {
120597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            // ignore
120697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        }
120797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        checkpoint(true);
120897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
120985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
121085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
121197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Wait for the daemons to create the new state.
121297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                while (!state.exists()) {
12131f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Check if a running daemon is dead.
12141f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    for (int i = 0; i < mDaemons.length; ++i) {
12151f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        String daemon = mDaemons[i];
1216088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                        if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
12172e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                            throw new IllegalStateException(daemon + " is dead");
121885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
121985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
122085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
122185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
122285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
122397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Now we are connected. Read and parse the new state.
1224c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yeh                String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
12255026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                if (parameters.length != 7) {
122697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot parse the state");
122797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
122897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
122997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the interface and the addresses in the config.
123097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                mConfig.interfaze = parameters[0].trim();
123185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
12324ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                mConfig.addLegacyAddresses(parameters[1]);
123397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the routes if they are not set in the config.
123497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
12354ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    mConfig.addLegacyRoutes(parameters[2]);
123697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
123797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
123897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the DNS servers if they are not set in the config.
123941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
124097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String dnsServers = parameters[3].trim();
124141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    if (!dnsServers.isEmpty()) {
124241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
124341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
124441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
124541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
124697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the search domains if they are not set in the config.
124797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
124897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String searchDomains = parameters[4].trim();
124997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    if (!searchDomains.isEmpty()) {
125097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
125197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
125297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
125397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
12545026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                // Add a throw route for the VPN server endpoint, if one was specified.
12555026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                String endpoint = parameters[5];
12565026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                if (!endpoint.isEmpty()) {
12575026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    try {
12585026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        InetAddress addr = InetAddress.parseNumericAddress(endpoint);
12595026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        if (addr instanceof Inet4Address) {
12605026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
12615026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        } else if (addr instanceof Inet6Address) {
12625026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
12635026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        } else {
12645026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
12655026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        }
12665026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    } catch (IllegalArgumentException e) {
12675026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
12685026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    }
12695026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                }
12705026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti
127197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Here is the last step and it must be done synchronously.
127241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                synchronized (Vpn.this) {
12732b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    // Set the start time
12742b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    mConfig.startTime = SystemClock.elapsedRealtime();
12752b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde
127641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    // Check if the thread is interrupted while we are waiting.
127741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    checkpoint(false);
127841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1279e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Check if the interface is gone while we are waiting.
1280c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    if (jniCheck(mConfig.interfaze) == 0) {
128134e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                        throw new IllegalStateException(mConfig.interfaze + " is gone");
128241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
1283e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
1284e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Now INetworkManagementEventObserver is watching our back.
1285c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    mInterface = mConfig.interfaze;
12866bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mVpnUsers = new ArrayList<UidRange>();
128742065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                    mAllowIPv4 = mConfig.allowIPv4;
128842065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                    mAllowIPv6 = mConfig.allowIPv6;
12896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
12906bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentConnect();
12912e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
12922e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    Log.i(TAG, "Connected!");
129341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
129485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } catch (Exception e) {
12952e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.i(TAG, "Aborting", e);
1296438406092ed71c658bf5a4e6ae2e7282fc4fab4dLorenzo Colitti                updateState(DetailedState.FAILED, e.getMessage());
1297e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                exit();
12982e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            } finally {
12995317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                // Kill the daemons if they fail to stop.
1300899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!initFinished) {
13015317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    for (String daemon : mDaemons) {
1302088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                        SystemService.stop(daemon);
13035317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    }
13045317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                }
13055317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh
13062e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                // Do not leave an unstable state.
1307899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
13086bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentDisconnect();
13092e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                }
131085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
131185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
1312899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1313899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        /**
1314899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * Monitor the daemons we started, moving to disconnected state if the
1315899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * underlying services fail.
1316899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         */
1317899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        private void monitorDaemons() {
1318899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            if (!mNetworkInfo.isConnected()) {
1319899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                return;
1320899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1321899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1322899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            try {
1323899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                while (true) {
1324899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    Thread.sleep(2000);
1325899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    for (int i = 0; i < mDaemons.length; i++) {
1326899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1327899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                            return;
1328899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        }
1329899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
1330899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1331899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            } catch (InterruptedException e) {
1332899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
1333899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            } finally {
1334899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                for (String daemon : mDaemons) {
1335899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    SystemService.stop(daemon);
1336899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1337899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
13386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                agentDisconnect();
1339899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1340899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
134185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
1342ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh}
1343