Vpn.java revision 3b3dd942ec6a0beaccd1cef0723d72786435d8f3
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;
2031a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensenimport static android.net.ConnectivityManager.NETID_UNSET;
215026279ce45ae78126046607a2634dc9dae93199Lorenzo Colittiimport static android.net.RouteInfo.RTN_THROW;
2260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colittiimport static android.net.RouteInfo.RTN_UNREACHABLE;
2331a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensenimport static android.os.UserHandle.PER_USER_RANGE;
2442065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandranimport static android.system.OsConstants.AF_INET;
2542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandranimport static android.system.OsConstants.AF_INET6;
26899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
27bc19c181c8c058c824e4fee907a05129e142c388Jeff Davidsonimport android.Manifest;
284ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.app.AppGlobals;
2905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidsonimport android.app.AppOpsManager;
3090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidsonimport android.app.PendingIntent;
311b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.content.BroadcastReceiver;
32199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ComponentName;
33ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Context;
34ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Intent;
351b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.content.IntentFilter;
36199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ServiceConnection;
37ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.ApplicationInfo;
38ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.PackageManager;
396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.content.pm.PackageManager.NameNotFoundException;
40199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.pm.ResolveInfo;
41c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.content.pm.UserInfo;
42899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.ConnectivityManager;
431b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.net.IConnectivityManager;
44ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.net.INetworkManagementEventObserver;
455026279ce45ae78126046607a2634dc9dae93199Lorenzo Colittiimport android.net.IpPrefix;
464ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.net.LinkAddress;
4782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.LinkProperties;
4885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocket;
4985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocketAddress;
50c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandranimport android.net.Network;
516bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkAgent;
526bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkCapabilities;
53899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.NetworkInfo;
546bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkInfo.DetailedState;
558cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandranimport android.net.NetworkMisc;
5682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.RouteInfo;
576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.UidRange;
58ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.Binder;
59c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yehimport android.os.FileUtils;
60199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.IBinder;
61899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.INetworkManagementService;
626bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.os.Looper;
63199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.Parcel;
64ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.ParcelFileDescriptor;
6585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.Process;
66899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.RemoteException;
6785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemClock;
68088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkeyimport android.os.SystemService;
6950cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackbornimport android.os.UserHandle;
70c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.os.UserManager;
7182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.Credentials;
7282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.KeyStore;
73e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensenimport android.text.TextUtils;
74ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.util.Log;
75ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
76c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport com.android.internal.annotations.GuardedBy;
772e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
7804ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig;
79f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tongimport com.android.internal.net.VpnInfo;
8082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport com.android.internal.net.VpnProfile;
81899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport com.android.server.net.BaseNetworkObserver;
82ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
8305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidsonimport libcore.io.IoUtils;
8405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
8597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.File;
866bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidsonimport java.io.IOException;
8797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.InputStream;
8885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream;
8982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport java.net.Inet4Address;
90f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandranimport java.net.Inet6Address;
91f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandranimport java.net.InetAddress;
92d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets;
936bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.ArrayList;
9441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yehimport java.util.Arrays;
956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.List;
960784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport java.util.SortedSet;
970784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport java.util.TreeSet;
981b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport java.util.concurrent.atomic.AtomicInteger;
9985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
100ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/**
101ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * @hide
102ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
1036bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenpublic class Vpn {
1046bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private static final String NETWORKTYPE = "VPN";
105899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private static final String TAG = "Vpn";
106899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private static final boolean LOGD = true;
1076bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
108899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    // TODO: create separate trackers for each unique VPN to support
109899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    // automated reconnection
110199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
1116bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private Context mContext;
1126bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkInfo mNetworkInfo;
1136bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private String mPackage;
1146bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private int mOwnerUID;
115c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private String mInterface;
116199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private Connection mConnection;
11785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private LegacyVpnRunner mLegacyVpnRunner;
11890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    private PendingIntent mStatusIntent;
11957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    private volatile boolean mEnableTeardown = true;
1206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final INetworkManagementService mNetd;
121c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private VpnConfig mConfig;
1226bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkAgent mNetworkAgent;
1236bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final Looper mLooper;
1246bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final NetworkCapabilities mNetworkCapabilities;
125c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
126c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    /* list of users using this VPN. */
127c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    @GuardedBy("this")
1286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private List<UidRange> mVpnUsers = null;
129c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private BroadcastReceiver mUserIntentReceiver = null;
130c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
1310784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Handle of user initiating VPN.
1320784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private final int mUserHandle;
133ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
1346bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public Vpn(Looper looper, Context context, INetworkManagementService netService,
135e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen            int userHandle) {
136ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext = context;
1376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetd = netService;
1380784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mUserHandle = userHandle;
1396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mLooper = looper;
1406bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
1416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mPackage = VpnConfig.LEGACY_VPN;
1420784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mOwnerUID = getAppUid(mPackage, mUserHandle);
143899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
144899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
145899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            netService.registerObserver(mObserver);
146899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
147899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            Log.wtf(TAG, "Problem registering observer", e);
148899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
1490784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        if (userHandle == UserHandle.USER_OWNER) {
150c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Owner's VPN also needs to handle restricted users
151c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mUserIntentReceiver = new BroadcastReceiver() {
152c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                @Override
153c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                public void onReceive(Context context, Intent intent) {
154c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    final String action = intent.getAction();
1550784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
156c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                            UserHandle.USER_NULL);
1570784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    if (userHandle == UserHandle.USER_NULL) return;
158c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
159c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    if (Intent.ACTION_USER_ADDED.equals(action)) {
1600784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        onUserAdded(userHandle);
161c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
1620784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        onUserRemoved(userHandle);
163c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    }
164c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
165c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            };
166c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
167c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            IntentFilter intentFilter = new IntentFilter();
168c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            intentFilter.addAction(Intent.ACTION_USER_ADDED);
169c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            intentFilter.addAction(Intent.ACTION_USER_REMOVED);
170c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mContext.registerReceiverAsUser(
171c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
172c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
1736bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
1746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
1756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
1766bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities = new NetworkCapabilities();
1776bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
1786bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
179899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
180899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
18157666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    /**
18257666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * Set if this object is responsible for watching for {@link NetworkInfo}
18357666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * teardown. When {@code false}, teardown is handled externally by someone
18457666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * else.
18557666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     */
18657666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    public void setEnableTeardown(boolean enableTeardown) {
18757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mEnableTeardown = enableTeardown;
18857666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    }
18957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
190899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    /**
191899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     * Update current state, dispaching event to listeners.
192899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     */
193899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private void updateState(DetailedState detailedState, String reason) {
194899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
195899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        mNetworkInfo.setDetailedState(detailedState, reason, null);
1966bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkAgent != null) {
1976bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
1986bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
199ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
200ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
201ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
202100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Prepare for a VPN application. This method is designed to solve
203100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * race conditions. It first compares the current prepared package
204100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * with {@code oldPackage}. If they are the same, the prepared
205100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * package is revoked and replaced with {@code newPackage}. If
206100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * {@code oldPackage} is {@code null}, the comparison is omitted.
207100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * If {@code newPackage} is the same package or {@code null}, the
208100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revocation is omitted. This method returns {@code true} if the
209100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * operation is succeeded.
210e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     *
211100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Legacy VPN is handled specially since it is not a real package.
212100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
213100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * it can be revoked by itself.
214100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     *
215100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param oldPackage The package name of the old VPN application.
216100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param newPackage The package name of the new VPN application.
217100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @return true if the operation is succeeded.
218ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
219100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh    public synchronized boolean prepare(String oldPackage, String newPackage) {
2200a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        if (oldPackage != null) {
2210a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson            if (getAppUid(oldPackage, mUserHandle) != mOwnerUID) {
2220a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // The package doesn't match. We return false (to obtain user consent) unless the
2230a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // user has already consented to that VPN package.
2240a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
2250a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    prepareInternal(oldPackage);
2260a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    return true;
2270a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                }
2280a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                return false;
2290a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson            } else if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
2300a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    && !isVpnUserPreConsented(oldPackage)) {
2310a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // Currently prepared VPN is revoked, so unprepare it and return false.
2320a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                prepareInternal(VpnConfig.LEGACY_VPN);
2330a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                return false;
23405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            }
235100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        }
236100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh
237100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return true if we do not need to revoke.
238be08587510edbc149c841638db721eb97d2351b6Jeff Davidson        if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
239be08587510edbc149c841638db721eb97d2351b6Jeff Davidson                getAppUid(newPackage, mUserHandle) == mOwnerUID)) {
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
24611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        prepareInternal(newPackage);
24711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        return true;
24811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    }
24911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson
25011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    /** Prepare the VPN for the given package. Does not perform permission checks. */
25111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    private void prepareInternal(String newPackage) {
25211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        long token = Binder.clearCallingIdentity();
25311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        try {
25411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            // Reset the interface.
25511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            if (mInterface != null) {
25611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mStatusIntent = null;
25711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                agentDisconnect();
25811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                jniReset(mInterface);
25911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mInterface = null;
26011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mVpnUsers = null;
26111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
26211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson
26311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            // Revoke the connection or stop LegacyVpnRunner.
26411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            if (mConnection != null) {
26511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                try {
26611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                    mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
26711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                            Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
26811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                } catch (Exception e) {
26911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                    // ignore
27011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                }
27111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mContext.unbindService(mConnection);
27211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mConnection = null;
27311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            } else if (mLegacyVpnRunner != null) {
27411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mLegacyVpnRunner.exit();
27511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mLegacyVpnRunner = null;
27611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
277ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
278199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            try {
27911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mNetd.denyProtect(mOwnerUID);
280199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            } catch (Exception e) {
28111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
282199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
28341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
28411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
28511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mPackage = newPackage;
28611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mOwnerUID = getAppUid(newPackage, mUserHandle);
28711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            try {
28811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mNetd.allowProtect(mOwnerUID);
28911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            } catch (Exception e) {
29011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
29111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
29211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mConfig = null;
2936bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
29411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            updateState(DetailedState.IDLE, "prepare");
2956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } finally {
2966bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Binder.restoreCallingIdentity(token);
2976bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
298ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
299ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
30005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    /**
3013b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee     * Set whether a package has the ability to launch VPNs without user intervention.
30205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson     */
3033b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee    public void setPackageAuthorization(String packageName, boolean authorized) {
30405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Check if the caller is authorized.
30505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        enforceControlPermission();
30605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
3073b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee        int uid = getAppUid(packageName, mUserHandle);
3083b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee        if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
3093b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            // Authorization for nonexistent packages (or fake ones) can't be updated.
31005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            return;
31105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        }
31205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
31305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        long token = Binder.clearCallingIdentity();
31405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        try {
31505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            AppOpsManager appOps =
31605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
3173b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
31805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
31905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        } catch (Exception e) {
3203b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
32105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        } finally {
32205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            Binder.restoreCallingIdentity(token);
32305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        }
32405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    }
32505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
32605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    private boolean isVpnUserPreConsented(String packageName) {
32705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        AppOpsManager appOps =
32805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
32905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
33005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Verify that the caller matches the given package and has permission to activate VPNs.
33105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
33205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                packageName) == AppOpsManager.MODE_ALLOWED;
33305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    }
33405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
3350784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private int getAppUid(String app, int userHandle) {
33605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        if (VpnConfig.LEGACY_VPN.equals(app)) {
3376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            return Process.myUid();
3386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
339fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
3406bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        int result;
3416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
3420784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            result = pm.getPackageUid(app, userHandle);
3436bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (NameNotFoundException e) {
3446bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            result = -1;
345fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        }
3466bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return result;
3476bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3486bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
3496bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public NetworkInfo getNetworkInfo() {
3506bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return mNetworkInfo;
3516bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
3526bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
35331a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen    public int getNetId() {
35431a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen        return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
35531a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen    }
35631a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen
35760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    private LinkProperties makeLinkProperties() {
35860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        boolean allowIPv4 = mConfig.allowIPv4;
35960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        boolean allowIPv6 = mConfig.allowIPv6;
36060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
3616bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        LinkProperties lp = new LinkProperties();
36260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
3636bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setInterfaceName(mInterface);
36442065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
36560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (mConfig.addresses != null) {
36660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            for (LinkAddress address : mConfig.addresses) {
36760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                lp.addLinkAddress(address);
36860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address.getAddress() instanceof Inet4Address;
36960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address.getAddress() instanceof Inet6Address;
37060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            }
3716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
37260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
37360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (mConfig.routes != null) {
37460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            for (RouteInfo route : mConfig.routes) {
37560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                lp.addRoute(route);
37660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                InetAddress address = route.getDestination().getAddress();
37760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address instanceof Inet4Address;
37860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address instanceof Inet6Address;
37960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            }
3806bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
38142065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
3826bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.dnsServers != null) {
3836bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String dnsServer : mConfig.dnsServers) {
38442065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                InetAddress address = InetAddress.parseNumericAddress(dnsServer);
38542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                lp.addDnsServer(address);
38660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address instanceof Inet4Address;
38760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address instanceof Inet6Address;
3886bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
3896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
39042065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
39160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (!allowIPv4) {
39260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
39360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
39460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (!allowIPv6) {
39560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
39660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
39760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
3986bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // Concatenate search domains into a string.
3996bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        StringBuilder buffer = new StringBuilder();
4006bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.searchDomains != null) {
4016bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String domain : mConfig.searchDomains) {
4026bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                buffer.append(domain).append(' ');
4036bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
4046bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
4056bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setDomains(buffer.toString().trim());
40642065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
40760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        // TODO: Stop setting the MTU in jniCreate and set it here.
40860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
40960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        return lp;
41060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    }
41160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
41260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    private void agentConnect() {
41360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        LinkProperties lp = makeLinkProperties();
41460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
41560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
41660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
41760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        } else {
41860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
41960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
42060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
4216bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo.setIsAvailable(true);
4226bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
42342065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
4248cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran        NetworkMisc networkMisc = new NetworkMisc();
42542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran        networkMisc.allowBypass = mConfig.allowBypass;
42642065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
4276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        long token = Binder.clearCallingIdentity();
4284ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
4296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
4308cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
43105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                            @Override
4326bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                            public void unwanted() {
4336bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                                // We are user controlled, not driven by NetworkRequest.
43405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                            }
4356bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        };
4364ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
4374ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
4384ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        }
43942065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
4400784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        addVpnUserLocked(mUserHandle);
4416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // If we are owner assign all Restricted Users to this VPN
4420784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        if (mUserHandle == UserHandle.USER_OWNER) {
4436bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            token = Binder.clearCallingIdentity();
4446bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            List<UserInfo> users;
4456bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            try {
4466bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                users = UserManager.get(mContext).getUsers();
4476bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            } finally {
4486bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                Binder.restoreCallingIdentity(token);
4496bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
4506bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (UserInfo user : users) {
4516bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                if (user.isRestricted()) {
4526bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    addVpnUserLocked(user.id);
4536bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                }
4546bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
4556bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
4566bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
4576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
4586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
4596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
4606bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        networkInfo.setIsAvailable(false);
4616bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
4626bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (networkAgent != null) {
4636bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            networkAgent.sendNetworkInfo(networkInfo);
4646bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
4656bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
4666bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
4676bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect(NetworkAgent networkAgent) {
4686bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
4696bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        agentDisconnect(networkInfo, networkAgent);
4706bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
4714ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
4726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect() {
4736bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkInfo.isConnected()) {
4746bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect(mNetworkInfo, mNetworkAgent);
4756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = null;
4766bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
477fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    }
478fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
479fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    /**
480e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * Establish a VPN network and return the file descriptor of the VPN
481e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * interface. This methods returns {@code null} if the application is
482100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revoked or not prepared.
483ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
484e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
485e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @return The file descriptor of the VPN interface.
486ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
48704ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
488ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check if the caller is already prepared.
489c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        UserManager mgr = UserManager.get(mContext);
4906bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (Binder.getCallingUid() != mOwnerUID) {
4917b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
492ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
4930a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        // Check to ensure consent hasn't been revoked since we were prepared.
4940a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        if (!isVpnUserPreConsented(mPackage)) {
4950a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson            return null;
4960a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        }
497fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Check if the service is properly declared.
498199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
499199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        intent.setClassName(mPackage, config.user);
5004ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        long token = Binder.clearCallingIdentity();
5014ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
502c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Restricted users are not allowed to create VPNs, they are tied to Owner
5030784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            UserInfo user = mgr.getUserInfo(mUserHandle);
50495778ffc58979d19ff9f4aaed396a6eca49cf698Nicolas Prevot            if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
50595778ffc58979d19ff9f4aaed396a6eca49cf698Nicolas Prevot                    new UserHandle(mUserHandle))) {
506c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                throw new SecurityException("Restricted users cannot establish VPNs");
507c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
508c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
5094ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
5100784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                                                                        null, 0, mUserHandle);
5114ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (info == null) {
5124ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
5134ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
5144ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
5154ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
5164ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
5174ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } catch (RemoteException e) {
5184ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
5194ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
5204ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
521199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
522fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
5234c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        // Save the old config in case we need to go back.
5244c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        VpnConfig oldConfig = mConfig;
5254c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        String oldInterface = mInterface;
5264c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        Connection oldConnection = mConnection;
5276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        NetworkAgent oldNetworkAgent = mNetworkAgent;
5286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent = null;
5296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        List<UidRange> oldUsers = mVpnUsers;
5304c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
531e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        // Configure the interface. Abort if any of these steps fails.
53297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
533ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
534899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            updateState(DetailedState.CONNECTING, "establish");
535c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            String interfaze = jniGetName(tun.getFd());
5364ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
537c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // TEMP use the old jni calls until there is support for netd address setting
5384ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            StringBuilder builder = new StringBuilder();
5394ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            for (LinkAddress address : config.addresses) {
5404ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                builder.append(" " + address);
54197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
5424ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
5434ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new IllegalArgumentException("At least one address must be specified");
54497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
545199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            Connection connection = new Connection();
546d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn            if (!mContext.bindServiceAsUser(intent, connection,
547d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
548d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    new UserHandle(mUserHandle))) {
549199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                throw new IllegalStateException("Cannot bind " + config.user);
550199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
5514c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
552199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = connection;
553c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = interfaze;
5544ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
5554ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Fill more values.
5564ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.user = mPackage;
5574ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.interfaze = mInterface;
558c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            config.startTime = SystemClock.elapsedRealtime();
559c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mConfig = config;
5604c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
5614ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Set up forwarding and DNS rules.
5626bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mVpnUsers = new ArrayList<UidRange>();
56342065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
5646bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentConnect();
5654ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
5664c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldConnection != null) {
5674c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                mContext.unbindService(oldConnection);
5684c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
5696bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // Remove the old tun's user forwarding rules
5706bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // The new tun's user rules have already been added so they will take over
5716bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // as rules are deleted. This prevents data leakage as the rules are moved over.
5726bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect(oldNetworkAgent);
5734c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldInterface != null && !oldInterface.equals(interfaze)) {
5744c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                jniReset(oldInterface);
5754c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
5766bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson
5776bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            try {
5786bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
5796bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            } catch (IOException e) {
5806bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                throw new IllegalStateException(
5816bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                        "Cannot set tunnel's fd as blocking=" + config.blocking, e);
5826bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            }
583ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (RuntimeException e) {
584065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey            IoUtils.closeQuietly(tun);
5856bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
5864c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            // restore old state
5874c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConfig = oldConfig;
5884c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConnection = oldConnection;
5894c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mVpnUsers = oldUsers;
5906bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = oldNetworkAgent;
5914c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mInterface = oldInterface;
592ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw e;
593ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
594199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
595c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        return tun;
596ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
597ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
598c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private boolean isRunningLocked() {
599c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return mNetworkAgent != null && mInterface != null;
600c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
601c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
602c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    // Returns true if the VPN has been established and the calling UID is its owner. Used to check
603c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    // that a call to mutate VPN state is admissible.
604c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    private boolean isCallerEstablishedOwnerLocked() {
605c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
606c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
607c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
6080784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Note: Return type guarantees results are deduped and sorted, which callers require.
6090784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
6100784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        SortedSet<Integer> uids = new TreeSet<Integer>();
6110784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        for (String app : packageNames) {
6120784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int uid = getAppUid(app, userHandle);
6130784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (uid != -1) uids.add(uid);
6140784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
6150784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        return uids;
6160784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    }
6170784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen
6186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
6190784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void addVpnUserLocked(int userHandle) {
620c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (mVpnUsers == null) {
621c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            throw new IllegalStateException("VPN is not active");
622c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
62369887e838814642a7ae78fc810656c7c8afc2a19Robert Greenwalt
6240784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        if (mConfig.allowedApplications != null) {
6250784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add ranges covering all UIDs for allowedApplications.
6260784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int start = -1, stop = -1;
6270784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            for (int uid : getAppsUids(mConfig.allowedApplications, userHandle)) {
6280784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                if (start == -1) {
6290784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid;
6300784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                } else if (uid != stop + 1) {
6310784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    mVpnUsers.add(new UidRange(start, stop));
6320784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid;
6330784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                }
6340784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                stop = uid;
6350784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
6360784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (start != -1) mVpnUsers.add(new UidRange(start, stop));
6370784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        } else if (mConfig.disallowedApplications != null) {
6380784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add all ranges for user skipping UIDs for disallowedApplications.
6390784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            final UidRange userRange = UidRange.createForUser(userHandle);
6400784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int start = userRange.start;
6410784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            for (int uid : getAppsUids(mConfig.disallowedApplications, userHandle)) {
6420784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                if (uid == start) {
6430784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start++;
6440784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                } else {
6450784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    mVpnUsers.add(new UidRange(start, uid - 1));
6460784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid + 1;
6470784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                }
6480784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
6490784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (start <= userRange.stop) mVpnUsers.add(new UidRange(start, userRange.stop));
6500784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        } else {
6510784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add all UIDs for the user.
6520784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            mVpnUsers.add(UidRange.createForUser(userHandle));
6530784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
65490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson
65590b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        prepareStatusIntent();
656c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
657c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
6580784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
6590784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // apply to userHandle.
6600784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private List<UidRange> uidRangesForUser(int userHandle) {
6610784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final UidRange userRange = UidRange.createForUser(userHandle);
6620784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final List<UidRange> ranges = new ArrayList<UidRange>();
6630784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        for (UidRange range : mVpnUsers) {
6640784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (range.start >= userRange.start && range.stop <= userRange.stop) {
6650784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                ranges.add(range);
6660784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
6670784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
6680784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        return ranges;
6690784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    }
6700784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen
6710784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void removeVpnUserLocked(int userHandle) {
672c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (mVpnUsers == null) {
67390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            throw new IllegalStateException("VPN is not active");
67490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
6750784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final List<UidRange> ranges = uidRangesForUser(userHandle);
67690b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (mNetworkAgent != null) {
6770784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
67890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
6790784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mVpnUsers.removeAll(ranges);
68090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        mStatusIntent = null;
681c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
682c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
6830784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void onUserAdded(int userHandle) {
684c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // If the user is restricted tie them to the owner's VPN
685c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        synchronized(Vpn.this) {
686c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserManager mgr = UserManager.get(mContext);
6870784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            UserInfo user = mgr.getUserInfo(userHandle);
688c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            if (user.isRestricted()) {
689c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                try {
6900784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    addVpnUserLocked(userHandle);
6916bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    if (mNetworkAgent != null) {
6920784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        final List<UidRange> ranges = uidRangesForUser(userHandle);
6930784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                        mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
6946bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    }
695c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                } catch (Exception e) {
696c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    Log.wtf(TAG, "Failed to add restricted user to owner", e);
697c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
698c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
699c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
700c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
701c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
7020784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void onUserRemoved(int userHandle) {
703c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // clean up if restricted
704c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        synchronized(Vpn.this) {
705c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            UserManager mgr = UserManager.get(mContext);
7060784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            UserInfo user = mgr.getUserInfo(userHandle);
707c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            if (user.isRestricted()) {
708c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                try {
7090784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    removeVpnUserLocked(userHandle);
710c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                } catch (Exception e) {
711c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                    Log.wtf(TAG, "Failed to remove restricted user to owner", e);
712c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
713c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
714c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
715c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
716c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
717bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    /**
718bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     * Return the configuration of the currently running VPN.
719bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     */
720bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    public VpnConfig getVpnConfig() {
721bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        enforceControlPermission();
722bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        return mConfig;
723bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    }
724bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker
725899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    @Deprecated
726899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    public synchronized void interfaceStatusChanged(String iface, boolean up) {
727899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
728899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mObserver.interfaceStatusChanged(iface, up);
729899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
730899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // ignored; target is local
731aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
732ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
733ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
734899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
735899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
736899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceStatusChanged(String interfaze, boolean up) {
737899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
738899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!up && mLegacyVpnRunner != null) {
739899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mLegacyVpnRunner.check(interfaze);
740899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
741199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
742ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
743ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
744899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
745899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceRemoved(String interfaze) {
746899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
747899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
74890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson                    mStatusIntent = null;
7496bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mVpnUsers = null;
750c4c7231eb6d1efa9ecd7b693f8328a76a04e8bbbPaul Jensen                    mConfig = null;
751899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mInterface = null;
752899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    if (mConnection != null) {
753899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mContext.unbindService(mConnection);
754899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mConnection = null;
7556bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        agentDisconnect();
756899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    } else if (mLegacyVpnRunner != null) {
757899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner.exit();
758899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner = null;
759899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
760899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
761899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
762899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
763899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    };
764db3c8678e5cbdfec011afaf25bde2091152c30adHaoyu Bai
765dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    private void enforceControlPermission() {
766bc19c181c8c058c824e4fee907a05129e142c388Jeff Davidson        mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
767dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    }
768dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh
769199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private class Connection implements ServiceConnection {
770199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        private IBinder mService;
771199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
772199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
773199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceConnected(ComponentName name, IBinder service) {
774199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = service;
775199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
776199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
777199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
778199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceDisconnected(ComponentName name) {
779199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = null;
780199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
781199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    }
782199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
78390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    private void prepareStatusIntent() {
78490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        final long token = Binder.clearCallingIdentity();
78590b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        try {
78690b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
78790b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        } finally {
78890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            Binder.restoreCallingIdentity(token);
78990b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
79090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    }
79190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson
792f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    public synchronized boolean addAddress(String address, int prefixLength) {
793c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
794f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            return false;
795f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
796f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        boolean success = jniAddAddress(mInterface, address, prefixLength);
797c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        mNetworkAgent.sendLinkProperties(makeLinkProperties());
798f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        return success;
799f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    }
800f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran
801f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    public synchronized boolean removeAddress(String address, int prefixLength) {
802c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
803f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            return false;
804f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
805f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        boolean success = jniDelAddress(mInterface, address, prefixLength);
806c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        mNetworkAgent.sendLinkProperties(makeLinkProperties());
807f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        return success;
808f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    }
809f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran
810c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized boolean setUnderlyingNetworks(Network[] networks) {
811c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
812c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return false;
813c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
814c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (networks == null) {
815c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            mConfig.underlyingNetworks = null;
816c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        } else {
817c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            mConfig.underlyingNetworks = new Network[networks.length];
818c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            for (int i = 0; i < networks.length; ++i) {
819c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                if (networks[i] == null) {
820c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                    mConfig.underlyingNetworks[i] = null;
821c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                } else {
822c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                    mConfig.underlyingNetworks[i] = new Network(networks[i].netId);
823c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                }
824c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            }
825c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
826c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return true;
827c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
828c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
829c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized Network[] getUnderlyingNetworks() {
830c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isRunningLocked()) {
831c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return null;
832c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
833c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return mConfig.underlyingNetworks;
834c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
835c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
836f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    /**
837f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     * This method should only be called by ConnectivityService. Because it doesn't
838f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     * have enough data to fill VpnInfo.primaryUnderlyingIface field.
839f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     */
840f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    public synchronized VpnInfo getVpnInfo() {
841f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        if (!isRunningLocked()) {
842f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong            return null;
843f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        }
844f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong
845f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        VpnInfo info = new VpnInfo();
846f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        info.ownerUid = mOwnerUID;
847f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        info.vpnIface = mInterface;
848f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        return info;
849f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    }
850f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong
851c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized boolean appliesToUid(int uid) {
852c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isRunningLocked()) {
853c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return false;
854c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
855c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        for (UidRange uidRange : mVpnUsers) {
856c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            if (uidRange.start <= uid && uid <= uidRange.stop) {
857c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                return true;
858c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            }
859c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
860c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return false;
861c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
862c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
86397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniCreate(int mtu);
864c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native String jniGetName(int tun);
86597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniSetAddresses(String interfaze, String addresses);
866c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniReset(String interfaze);
867c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native int jniCheck(String interfaze);
868f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
869f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
87085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
87141fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti    private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
87241fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        for (RouteInfo route : prop.getAllRoutes()) {
87382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            // Currently legacy VPN only works on IPv4.
87482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
87541fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti                return route;
87682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            }
87782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
87882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
87941fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        throw new IllegalStateException("Unable to find IPv4 default gateway");
88082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
88182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
88285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
88382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * Start legacy VPN, controlling native daemons as needed. Creates a
88482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * secondary thread to perform connection work, returning quickly.
885b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     *
886b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Should only be called to respond to Binder requests as this enforces caller permission. Use
887b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
888b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * permission check only when the caller is trusted (or the call is initiated by the system).
88985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
89082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
8915a6bdc46e2fdc8cfd930396773dd89efd19fa1f1Robert Greenwalt        enforceControlPermission();
892b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        long token = Binder.clearCallingIdentity();
893b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        try {
894b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            startLegacyVpnPrivileged(profile, keyStore, egress);
895b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        } finally {
896b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            Binder.restoreCallingIdentity(token);
897b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        }
898b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    }
899b21298a686b04d55ff97223dd317497845713f4bJeff Davidson
900b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    /**
901b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
902b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * permissions under the assumption that the caller is the system.
903b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     *
904b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Callers are responsible for checking permissions if needed.
905b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     */
906b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
907b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            LinkProperties egress) {
908b9594ce9ebb3f5f303a280f04312ae5754ce3560Kenny Root        if (!keyStore.isUnlocked()) {
90982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            throw new IllegalStateException("KeyStore isn't unlocked");
91082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
911f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        UserManager mgr = UserManager.get(mContext);
9120784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        UserInfo user = mgr.getUserInfo(mUserHandle);
91395778ffc58979d19ff9f4aaed396a6eca49cf698Nicolas Prevot        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
91495778ffc58979d19ff9f4aaed396a6eca49cf698Nicolas Prevot                    new UserHandle(mUserHandle))) {
915f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds            throw new SecurityException("Restricted users cannot establish VPNs");
916f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        }
91782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
91841fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
91941fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
92041fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String iface = ipv4DefaultRoute.getInterface();
92182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
92282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Load certificates.
92382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String privateKey = "";
92482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String userCert = "";
92582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String caCert = "";
92682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String serverCert = "";
92782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecUserCert.isEmpty()) {
92882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
92982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
930d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
93182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
93282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecCaCert.isEmpty()) {
93382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
934d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
93582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
93682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecServerCert.isEmpty()) {
93782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
938d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
93982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
94082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
94182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            throw new IllegalStateException("Cannot load credentials");
94282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
94382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
94482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for racoon.
94582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] racoon = null;
94682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
94782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
94882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
94982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
95082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, "1701",
95182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
95282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
95382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
95482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
95582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udprsa", privateKey, userCert,
95682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, "1701",
95782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
95882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
95982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
96082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
96182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
96282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
96382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
96482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
96582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
96682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
96782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthrsa", privateKey, userCert,
96882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
96982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
97082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
97182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
97282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
97382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "hybridrsa",
97482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
97582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
97682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
97782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
97882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
97982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for mtpd.
98082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] mtpd = null;
98182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
98282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_PPTP:
98382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
98482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "pptp", profile.server, "1723",
98582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
98682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
98782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
98882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    (profile.mppe ? "+mppe" : "nomppe"),
98982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
99082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
99182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
99282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
99382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
99482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
99582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
99682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
99782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
99882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
99982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
100082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
1001899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
100282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        VpnConfig config = new VpnConfig();
1003899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        config.legacy = true;
100482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.user = profile.key;
100582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.interfaze = iface;
100682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.session = profile.name;
10074ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
10084ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        config.addLegacyRoutes(profile.routes);
100982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.dnsServers.isEmpty()) {
101082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
101182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
101282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.searchDomains.isEmpty()) {
101382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
101482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
101582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        startLegacyVpn(config, racoon, mtpd);
101682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
101782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
101882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
1019b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        stopLegacyVpnPrivileged();
1020899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1021b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        // Prepare for the new request.
1022b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        prepareInternal(VpnConfig.LEGACY_VPN);
1023899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        updateState(DetailedState.CONNECTING, "startLegacyVpn");
102485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10252e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Start a new LegacyVpnRunner and we are done!
1026100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
1027100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner.start();
102885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
102985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
1030b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    /** Stop legacy VPN. Permissions must be checked by callers. */
1031b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    public synchronized void stopLegacyVpnPrivileged() {
1032899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner != null) {
1033899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner.exit();
1034899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner = null;
1035899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1036899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (LegacyVpnRunner.TAG) {
1037899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // wait for old thread to completely finish before spinning up
1038899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // new instance, otherwise state updates can be out of order.
1039899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1040899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
1041899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
1042899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
104385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
10442e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Return the information of the current ongoing legacy VPN.
10452e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     */
10462e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
1047dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // Check if the caller is authorized.
1048dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
104908bbca040fa921b99493cd9967453ed90b1b710asj.cha        return getLegacyVpnInfoPrivileged();
105008bbca040fa921b99493cd9967453ed90b1b710asj.cha    }
105108bbca040fa921b99493cd9967453ed90b1b710asj.cha
105208bbca040fa921b99493cd9967453ed90b1b710asj.cha    /**
105308bbca040fa921b99493cd9967453ed90b1b710asj.cha     * Return the information of the current ongoing legacy VPN.
105408bbca040fa921b99493cd9967453ed90b1b710asj.cha     * Callers are responsible for checking permissions if needed.
105508bbca040fa921b99493cd9967453ed90b1b710asj.cha     */
105608bbca040fa921b99493cd9967453ed90b1b710asj.cha    public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
1057899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner == null) return null;
1058899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1059899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        final LegacyVpnInfo info = new LegacyVpnInfo();
1060c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        info.key = mConfig.user;
1061899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
106290b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (mNetworkInfo.isConnected()) {
106390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            info.intent = mStatusIntent;
106490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
1065899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        return info;
10662e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    }
10672e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
106869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public VpnConfig getLegacyVpnConfig() {
106969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (mLegacyVpnRunner != null) {
1070c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            return mConfig;
107169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else {
107269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            return null;
107369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
107469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
107569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
10762e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    /**
107785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Bringing up a VPN connection takes time, and that is all this thread
107885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * does. Here we have plenty of time. The only thing we need to take
107985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * care of is responding to interruptions as soon as possible. Otherwise
108085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * requests will be piled up. This can be done in a Handler as a state
108185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * machine, but it is much easier to read in the current form.
108285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
108385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private class LegacyVpnRunner extends Thread {
108485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String TAG = "LegacyVpnRunner";
108585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10861f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh        private final String[] mDaemons;
108785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[][] mArguments;
10885317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh        private final LocalSocket[] mSockets;
108953c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt        private final String mOuterInterface;
10901b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final AtomicInteger mOuterConnection =
10911b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                new AtomicInteger(ConnectivityManager.TYPE_NONE);
10922e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
109385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private long mTimer = -1;
109485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
10951b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        /**
10961b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         * Watch for the outer connection (passing in the constructor) going away.
10971b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         */
10981b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
10991b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            @Override
11001b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            public void onReceive(Context context, Intent intent) {
110157666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey                if (!mEnableTeardown) return;
110257666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
11031b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
11041b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
11051b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
11061b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        NetworkInfo info = (NetworkInfo)intent.getExtra(
11071b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                ConnectivityManager.EXTRA_NETWORK_INFO);
11081b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        if (info != null && !info.isConnectedOrConnecting()) {
11091b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            try {
11101b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                mObserver.interfaceStatusChanged(mOuterInterface, false);
11111b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            } catch (RemoteException e) {}
11121b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        }
11131b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    }
11141b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                }
11151b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
11161b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        };
11171b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
111841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
111985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            super(TAG);
112041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mConfig = config;
112141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mDaemons = new String[] {"racoon", "mtpd"};
1122899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // TODO: clear arguments from memory once launched
112341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mArguments = new String[][] {racoon, mtpd};
11245317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            mSockets = new LocalSocket[mDaemons.length];
112553c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt
112653c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // This is the interface which VPN is running on,
112753c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // mConfig.interfaze will change to point to OUR
112853c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // internal interface soon. TODO - add inner/outer to mconfig
11291b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
11304ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // we will leave the VPN up.  We should check that it's still there/connected after
11311b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // registering
113253c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            mOuterInterface = mConfig.interfaze;
11331b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
1134e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen            if (!TextUtils.isEmpty(mOuterInterface)) {
1135e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                final ConnectivityManager cm = ConnectivityManager.from(mContext);
1136e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                for (Network network : cm.getAllNetworks()) {
1137e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                    final LinkProperties lp = cm.getLinkProperties(network);
1138e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                    if (lp != null && mOuterInterface.equals(lp.getInterfaceName())) {
1139e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                        final NetworkInfo networkInfo = cm.getNetworkInfo(network);
1140e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                        if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
1141e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                    }
1142e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                }
11431b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
11441b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
11451b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            IntentFilter filter = new IntentFilter();
11461b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
11471b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            mContext.registerReceiver(mBroadcastReceiver, filter);
114841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
114941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1150aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        public void check(String interfaze) {
115153c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            if (interfaze.equals(mOuterInterface)) {
1152aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
1153aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                exit();
1154aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            }
1155aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
1156aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh
115741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public void exit() {
11585317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            // We assume that everything is reset after stopping the daemons.
115997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            interrupt();
11605317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            for (LocalSocket socket : mSockets) {
1161065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey                IoUtils.closeQuietly(socket);
116241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            }
11636bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
11641b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            try {
11651b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mContext.unregisterReceiver(mBroadcastReceiver);
11661b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            } catch (IllegalArgumentException e) {}
11672e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
11682e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
116985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        @Override
117085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public void run() {
117185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Wait for the previous thread since it has been interrupted.
11722e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            Log.v(TAG, "Waiting");
117385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            synchronized (TAG) {
11742e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.v(TAG, "Executing");
117585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                execute();
1176899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                monitorDaemons();
117785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
117885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
117985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
118085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void checkpoint(boolean yield) throws InterruptedException {
118185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            long now = SystemClock.elapsedRealtime();
118285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            if (mTimer == -1) {
118385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                mTimer = now;
118485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(1);
11857ef8611b5f3a893a46c7b9e22bdd8ab252e373ffChia-chi Yeh            } else if (now - mTimer <= 60000) {
118685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(yield ? 200 : 1);
118785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else {
1188899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.FAILED, "checkpoint");
118997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                throw new IllegalStateException("Time is up");
119085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
119185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
119285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
119385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void execute() {
119485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Catch all exceptions so we can clean up few things.
1195899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            boolean initFinished = false;
119685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            try {
119785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Initialize the timer.
119885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                checkpoint(false);
119985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
12001f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Wait for the daemons to stop.
12011f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (String daemon : mDaemons) {
1202088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isStopped(daemon)) {
120385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
120485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
120585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
120685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
120797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Clear the previous state.
120897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                File state = new File("/data/misc/vpn/state");
120997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                state.delete();
121097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (state.exists()) {
121197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot delete the state");
121285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1213c1872732922214de80f790e14865e41dd1b98203Chia-chi Yeh                new File("/data/misc/vpn/abort").delete();
1214899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                initFinished = true;
121585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
1216e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // Check if we need to restart any of the daemons.
121785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                boolean restart = false;
121885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String[] arguments : mArguments) {
121985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    restart = restart || (arguments != null);
122085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
122185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                if (!restart) {
12226bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentDisconnect();
122385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    return;
122485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1225899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.CONNECTING, "execute");
122685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
12271f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Start the daemon with arguments.
12281f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (int i = 0; i < mDaemons.length; ++i) {
122985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String[] arguments = mArguments[i];
123085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    if (arguments == null) {
123185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        continue;
123285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
123385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
12341f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Start the daemon.
12351f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String daemon = mDaemons[i];
1236088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    SystemService.start(daemon);
123785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
12381f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Wait for the daemon to start.
1239088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isRunning(daemon)) {
124085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
124185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
124285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
124385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Create the control socket.
12445317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i] = new LocalSocket();
124585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocketAddress address = new LocalSocketAddress(
12461f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                            daemon, LocalSocketAddress.Namespace.RESERVED);
124785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
124885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the socket to connect.
124985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (true) {
125085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        try {
12515317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                            mSockets[i].connect(address);
125285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            break;
125385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        } catch (Exception e) {
125485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            // ignore
125585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
125685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
125785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
12585317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i].setSoTimeout(500);
125985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
126085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send over the arguments.
12615317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    OutputStream out = mSockets[i].getOutputStream();
126285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (String argument : arguments) {
1263d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                        byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
12645317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                        if (bytes.length >= 0xFFFF) {
126597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            throw new IllegalArgumentException("Argument is too large");
126685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
12671f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length >> 8);
12681f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length);
12691f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes);
127085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(false);
127185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
12725317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
12735317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
12741f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.flush();
127597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
127697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    // Wait for End-of-File.
12775317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    InputStream in = mSockets[i].getInputStream();
127897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    while (true) {
127997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        try {
128097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            if (in.read() == -1) {
128197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                                break;
128297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            }
128397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        } catch (Exception e) {
128497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            // ignore
128597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        }
128697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        checkpoint(true);
128797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
128885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
128985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
129097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Wait for the daemons to create the new state.
129197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                while (!state.exists()) {
12921f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Check if a running daemon is dead.
12931f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    for (int i = 0; i < mDaemons.length; ++i) {
12941f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        String daemon = mDaemons[i];
1295088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                        if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
12962e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                            throw new IllegalStateException(daemon + " is dead");
129785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
129885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
129985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
130085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
130185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
130297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Now we are connected. Read and parse the new state.
1303c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yeh                String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
13045026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                if (parameters.length != 7) {
130597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot parse the state");
130697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
130797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
130897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the interface and the addresses in the config.
130997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                mConfig.interfaze = parameters[0].trim();
131085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
13114ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                mConfig.addLegacyAddresses(parameters[1]);
131297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the routes if they are not set in the config.
131397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
13144ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    mConfig.addLegacyRoutes(parameters[2]);
131597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
131697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
131797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the DNS servers if they are not set in the config.
131841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
131997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String dnsServers = parameters[3].trim();
132041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    if (!dnsServers.isEmpty()) {
132141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
132241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
132341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
132441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
132597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the search domains if they are not set in the config.
132697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
132797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String searchDomains = parameters[4].trim();
132897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    if (!searchDomains.isEmpty()) {
132997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
133097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
133197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
133297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
13335026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                // Add a throw route for the VPN server endpoint, if one was specified.
13345026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                String endpoint = parameters[5];
13355026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                if (!endpoint.isEmpty()) {
13365026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    try {
13375026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        InetAddress addr = InetAddress.parseNumericAddress(endpoint);
13385026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        if (addr instanceof Inet4Address) {
13395026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
13405026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        } else if (addr instanceof Inet6Address) {
13415026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
13425026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        } else {
13435026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
13445026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        }
13455026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    } catch (IllegalArgumentException e) {
13465026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
13475026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    }
13485026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                }
13495026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti
135097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Here is the last step and it must be done synchronously.
135141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                synchronized (Vpn.this) {
13522b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    // Set the start time
13532b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    mConfig.startTime = SystemClock.elapsedRealtime();
13542b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde
135541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    // Check if the thread is interrupted while we are waiting.
135641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    checkpoint(false);
135741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1358e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Check if the interface is gone while we are waiting.
1359c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    if (jniCheck(mConfig.interfaze) == 0) {
136034e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                        throw new IllegalStateException(mConfig.interfaze + " is gone");
136141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
1362e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
1363e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Now INetworkManagementEventObserver is watching our back.
1364c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    mInterface = mConfig.interfaze;
13656bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mVpnUsers = new ArrayList<UidRange>();
13666bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
13676bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentConnect();
13682e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
13692e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    Log.i(TAG, "Connected!");
137041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
137185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } catch (Exception e) {
13722e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.i(TAG, "Aborting", e);
1373438406092ed71c658bf5a4e6ae2e7282fc4fab4dLorenzo Colitti                updateState(DetailedState.FAILED, e.getMessage());
1374e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                exit();
13752e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            } finally {
13765317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                // Kill the daemons if they fail to stop.
1377899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!initFinished) {
13785317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    for (String daemon : mDaemons) {
1379088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                        SystemService.stop(daemon);
13805317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    }
13815317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                }
13825317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh
13832e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                // Do not leave an unstable state.
1384899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
13856bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentDisconnect();
13862e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                }
138785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
138885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
1389899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1390899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        /**
1391899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * Monitor the daemons we started, moving to disconnected state if the
1392899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * underlying services fail.
1393899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         */
1394899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        private void monitorDaemons() {
1395899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            if (!mNetworkInfo.isConnected()) {
1396899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                return;
1397899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1398899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1399899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            try {
1400899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                while (true) {
1401899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    Thread.sleep(2000);
1402899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    for (int i = 0; i < mDaemons.length; i++) {
1403899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1404899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                            return;
1405899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        }
1406899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
1407899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1408899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            } catch (InterruptedException e) {
1409899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
1410899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            } finally {
1411899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                for (String daemon : mDaemons) {
1412899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    SystemService.stop(daemon);
1413899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1414899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
14156bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                agentDisconnect();
1416899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1417899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
141885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
1419ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh}
1420