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