1ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/* 2ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Copyright (C) 2011 The Android Open Source Project 3ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * 4ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License"); 5ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * you may not use this file except in compliance with the License. 6ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * You may obtain a copy of the License at 7ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * 8ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * http://www.apache.org/licenses/LICENSE-2.0 9ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * 10ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Unless required by applicable law or agreed to in writing, software 11ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS, 12ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * See the License for the specific language governing permissions and 14ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * limitations under the License. 15ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */ 16ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 17ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehpackage com.android.server.connectivity; 18ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 19899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport static android.Manifest.permission.BIND_VPN_SERVICE; 20899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 21ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.app.Notification; 22ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.app.NotificationManager; 23899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.app.PendingIntent; 24199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ComponentName; 25ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Context; 26ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Intent; 27199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ServiceConnection; 28ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.ApplicationInfo; 29ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.PackageManager; 30199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.pm.ResolveInfo; 31ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.Bitmap; 32ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.Canvas; 33ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.drawable.Drawable; 34899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.BaseNetworkStateTracker; 35899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.ConnectivityManager; 36ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.net.INetworkManagementEventObserver; 3782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.LinkProperties; 3885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocket; 3985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocketAddress; 40899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.NetworkInfo; 4182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.RouteInfo; 42899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.NetworkInfo.DetailedState; 43ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.Binder; 44c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yehimport android.os.FileUtils; 45199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.IBinder; 46899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.INetworkManagementService; 47199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.Parcel; 48ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.ParcelFileDescriptor; 4985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.Process; 50899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.RemoteException; 5185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemClock; 52088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkeyimport android.os.SystemService; 5350cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackbornimport android.os.UserHandle; 5482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.Credentials; 5582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.KeyStore; 56ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.util.Log; 5782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.widget.Toast; 58ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 59ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport com.android.internal.R; 602e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yehimport com.android.internal.net.LegacyVpnInfo; 6104ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig; 6282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport com.android.internal.net.VpnProfile; 63899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport com.android.internal.util.Preconditions; 64ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport com.android.server.ConnectivityService.VpnCallback; 65899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport com.android.server.net.BaseNetworkObserver; 66ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 6797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.File; 6897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.InputStream; 6985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream; 7082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport java.net.Inet4Address; 7182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport java.net.InetAddress; 7285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.nio.charset.Charsets; 7341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yehimport java.util.Arrays; 7485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 75065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkeyimport libcore.io.IoUtils; 76065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey 77ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/** 78ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * @hide 79ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */ 80899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeypublic class Vpn extends BaseNetworkStateTracker { 81899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey private static final String TAG = "Vpn"; 82899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey private static final boolean LOGD = true; 83899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 84899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // TODO: create separate trackers for each unique VPN to support 85899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // automated reconnection 86199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh 87ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh private final VpnCallback mCallback; 88ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 89c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh private String mPackage = VpnConfig.LEGACY_VPN; 90c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh private String mInterface; 91199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh private Connection mConnection; 9285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh private LegacyVpnRunner mLegacyVpnRunner; 93899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey private PendingIntent mStatusIntent; 9469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey private boolean mEnableNotif = true; 95ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 96899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey public Vpn(Context context, VpnCallback callback, INetworkManagementService netService) { 97899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // TODO: create dedicated TYPE_VPN network type 98899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey super(ConnectivityManager.TYPE_DUMMY); 99ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh mContext = context; 100ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh mCallback = callback; 101899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 102899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey try { 103899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey netService.registerObserver(mObserver); 104899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } catch (RemoteException e) { 105899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey Log.wtf(TAG, "Problem registering observer", e); 106899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 107899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 108899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 10969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey public void setEnableNotifications(boolean enableNotif) { 11069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey mEnableNotif = enableNotif; 11169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey } 11269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey 113899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey @Override 114899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey protected void startMonitoringInternal() { 115899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // Ignored; events are sent through callbacks for now 116899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 117899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 118899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey @Override 119899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey public boolean teardown() { 120899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // TODO: finish migration to unique tracker for each VPN 121899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey throw new UnsupportedOperationException(); 122899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 123899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 124899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey @Override 125899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey public boolean reconnect() { 126899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // TODO: finish migration to unique tracker for each VPN 127899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey throw new UnsupportedOperationException(); 128899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 129899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 130899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey @Override 131899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey public String getTcpBufferSizesPropName() { 132899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey return PROP_TCP_BUFFER_UNKNOWN; 133899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 134899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 135899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey /** 136899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey * Update current state, dispaching event to listeners. 137899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey */ 138899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey private void updateState(DetailedState detailedState, String reason) { 139899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason); 140899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mNetworkInfo.setDetailedState(detailedState, reason, null); 141899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mCallback.onStateChanged(new NetworkInfo(mNetworkInfo)); 142ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 143ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 144ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh /** 145100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * Prepare for a VPN application. This method is designed to solve 146100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * race conditions. It first compares the current prepared package 147100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * with {@code oldPackage}. If they are the same, the prepared 148100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * package is revoked and replaced with {@code newPackage}. If 149100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * {@code oldPackage} is {@code null}, the comparison is omitted. 150100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * If {@code newPackage} is the same package or {@code null}, the 151100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * revocation is omitted. This method returns {@code true} if the 152100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * operation is succeeded. 153e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh * 154100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * Legacy VPN is handled specially since it is not a real package. 155100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and 156100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * it can be revoked by itself. 157100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * 158100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * @param oldPackage The package name of the old VPN application. 159100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * @param newPackage The package name of the new VPN application. 160100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * @return true if the operation is succeeded. 161ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */ 162100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh public synchronized boolean prepare(String oldPackage, String newPackage) { 163100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh // Return false if the package does not match. 164c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh if (oldPackage != null && !oldPackage.equals(mPackage)) { 165100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh return false; 166100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh } 167100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh 168100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh // Return true if we do not need to revoke. 169100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh if (newPackage == null || 170c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) { 171100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh return true; 172ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 173ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 174dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh // Check if the caller is authorized. 175dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh enforceControlPermission(); 1767b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh 177ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh // Reset the interface and hide the notification. 178c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh if (mInterface != null) { 179c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh jniReset(mInterface); 180899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey final long token = Binder.clearCallingIdentity(); 181899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey try { 182899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mCallback.restore(); 183899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey hideNotification(); 184899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } finally { 185899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey Binder.restoreCallingIdentity(token); 186899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 187c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh mInterface = null; 188ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 189ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 190fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh // Revoke the connection or stop LegacyVpnRunner. 191199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh if (mConnection != null) { 192199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh try { 193199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION, 194199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh Parcel.obtain(), null, IBinder.FLAG_ONEWAY); 195199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } catch (Exception e) { 196199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh // ignore 197199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 198199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh mContext.unbindService(mConnection); 199199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh mConnection = null; 200e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh } else if (mLegacyVpnRunner != null) { 20141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh mLegacyVpnRunner.exit(); 20241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh mLegacyVpnRunner = null; 20341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh } 20441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh 205c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh Log.i(TAG, "Switched from " + mPackage + " to " + newPackage); 206c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh mPackage = newPackage; 207899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.IDLE, "prepare"); 208100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh return true; 209ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 210ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 211ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh /** 212fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh * Protect a socket from routing changes by binding it to the given 213fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh * interface. The socket is NOT closed by this method. 214fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh * 215fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh * @param socket The socket to be bound. 216899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey * @param interfaze The name of the interface. 217fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh */ 218fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception { 219fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh PackageManager pm = mContext.getPackageManager(); 220fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh ApplicationInfo app = pm.getApplicationInfo(mPackage, 0); 221fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh if (Binder.getCallingUid() != app.uid) { 222fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh throw new SecurityException("Unauthorized Caller"); 223fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh } 224fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh jniProtect(socket.getFd(), interfaze); 225fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh } 226fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh 227fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh /** 228e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh * Establish a VPN network and return the file descriptor of the VPN 229e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh * interface. This methods returns {@code null} if the application is 230100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh * revoked or not prepared. 231ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * 232e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh * @param config The parameters to configure the network. 233e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh * @return The file descriptor of the VPN interface. 234ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */ 23504ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh public synchronized ParcelFileDescriptor establish(VpnConfig config) { 236ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh // Check if the caller is already prepared. 237ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh PackageManager pm = mContext.getPackageManager(); 238ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh ApplicationInfo app = null; 239ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh try { 240c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh app = pm.getApplicationInfo(mPackage, 0); 241ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } catch (Exception e) { 2427b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh return null; 243ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 244ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh if (Binder.getCallingUid() != app.uid) { 2457b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh return null; 246ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 247ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 248fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh // Check if the service is properly declared. 249199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE); 250199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh intent.setClassName(mPackage, config.user); 251199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh ResolveInfo info = pm.resolveService(intent, 0); 252199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh if (info == null) { 253199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh throw new SecurityException("Cannot find " + config.user); 254199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 255199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) { 256199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE); 257199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 258fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh 259a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh // Load the label. 260a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh String label = app.loadLabel(pm).toString(); 261a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh 262a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh // Load the icon and convert it into a bitmap. 263a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh Drawable icon = app.loadIcon(pm); 264a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh Bitmap bitmap = null; 265a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) { 266a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh int width = mContext.getResources().getDimensionPixelSize( 267a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh android.R.dimen.notification_large_icon_width); 268a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh int height = mContext.getResources().getDimensionPixelSize( 269a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh android.R.dimen.notification_large_icon_height); 270a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh icon.setBounds(0, 0, width, height); 271a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 2726311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn Canvas c = new Canvas(bitmap); 2736311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn icon.draw(c); 2746311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn c.setBitmap(null); 275a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh } 276a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh 277e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh // Configure the interface. Abort if any of these steps fails. 27897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu)); 279ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh try { 280899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.CONNECTING, "establish"); 281c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh String interfaze = jniGetName(tun.getFd()); 28297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh if (jniSetAddresses(interfaze, config.addresses) < 1) { 28397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh throw new IllegalArgumentException("At least one address must be specified"); 28497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 28597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh if (config.routes != null) { 28697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh jniSetRoutes(interfaze, config.routes); 28797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 288199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh Connection connection = new Connection(); 289199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) { 290199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh throw new IllegalStateException("Cannot bind " + config.user); 291199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 292199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh if (mConnection != null) { 293199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh mContext.unbindService(mConnection); 294199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 295c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh if (mInterface != null && !mInterface.equals(interfaze)) { 296c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh jniReset(mInterface); 297ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 298199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh mConnection = connection; 299c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh mInterface = interfaze; 300ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } catch (RuntimeException e) { 301899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.FAILED, "establish"); 302065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey IoUtils.closeQuietly(tun); 303ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh throw e; 304ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 305199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh Log.i(TAG, "Established by " + config.user + " on " + mInterface); 306ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 3078909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh // Fill more values. 308fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh config.user = mPackage; 309c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh config.interfaze = mInterface; 3108909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh 311fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh // Override DNS servers and show the notification. 312899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey final long token = Binder.clearCallingIdentity(); 313899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey try { 314899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mCallback.override(config.dnsServers, config.searchDomains); 315899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey showNotification(config, label, bitmap); 316899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } finally { 317899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey Binder.restoreCallingIdentity(token); 318899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 319899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // TODO: ensure that contract class eventually marks as connected 320899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.AUTHENTICATING, "establish"); 321c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh return tun; 322ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 323ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 324899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey @Deprecated 325899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey public synchronized void interfaceStatusChanged(String iface, boolean up) { 326899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey try { 327899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mObserver.interfaceStatusChanged(iface, up); 328899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } catch (RemoteException e) { 329899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // ignored; target is local 330aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh } 331ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 332ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 333899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() { 334899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey @Override 335899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey public void interfaceStatusChanged(String interfaze, boolean up) { 336899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey synchronized (Vpn.this) { 337899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (!up && mLegacyVpnRunner != null) { 338899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mLegacyVpnRunner.check(interfaze); 339899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 340199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 341ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 342ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 343899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey @Override 344899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey public void interfaceRemoved(String interfaze) { 345899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey synchronized (Vpn.this) { 346899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) { 347899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey final long token = Binder.clearCallingIdentity(); 348899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey try { 349899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mCallback.restore(); 350899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey hideNotification(); 351899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } finally { 352899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey Binder.restoreCallingIdentity(token); 353899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 354899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mInterface = null; 355899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (mConnection != null) { 356899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mContext.unbindService(mConnection); 357899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mConnection = null; 358899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.DISCONNECTED, "interfaceRemoved"); 359899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } else if (mLegacyVpnRunner != null) { 360899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mLegacyVpnRunner.exit(); 361899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mLegacyVpnRunner = null; 362899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 363899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 364899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 365899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 366899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey }; 367db3c8678e5cbdfec011afaf25bde2091152c30adHaoyu Bai 368dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh private void enforceControlPermission() { 369dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh // System user is allowed to control VPN. 370dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh if (Binder.getCallingUid() == Process.SYSTEM_UID) { 371dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh return; 372dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh } 373dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh 374dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh try { 375dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh // System dialogs are also allowed to control VPN. 376dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh PackageManager pm = mContext.getPackageManager(); 377dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0); 378dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh if (Binder.getCallingUid() == app.uid) { 379dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh return; 380dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh } 381dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh } catch (Exception e) { 382dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh // ignore 383dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh } 384dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh 385dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh throw new SecurityException("Unauthorized Caller"); 386dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh } 387dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh 388199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh private class Connection implements ServiceConnection { 389199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh private IBinder mService; 390199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh 391199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh @Override 392199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh public void onServiceConnected(ComponentName name, IBinder service) { 393199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh mService = service; 394199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 395199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh 396199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh @Override 397199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh public void onServiceDisconnected(ComponentName name) { 398199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh mService = null; 399199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 400199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh } 401199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh 402a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh private void showNotification(VpnConfig config, String label, Bitmap icon) { 40369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey if (!mEnableNotif) return; 404899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config); 405899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 406ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh NotificationManager nm = (NotificationManager) 407ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh mContext.getSystemService(Context.NOTIFICATION_SERVICE); 408ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 409ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh if (nm != null) { 410a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh String title = (label == null) ? mContext.getString(R.string.vpn_title) : 411a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh mContext.getString(R.string.vpn_title_long, label); 41234e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh String text = (config.session == null) ? mContext.getString(R.string.vpn_text) : 41334e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh mContext.getString(R.string.vpn_text_long, config.session); 4142e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh config.startTime = SystemClock.elapsedRealtime(); 415a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh 416ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh Notification notification = new Notification.Builder(mContext) 417ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh .setSmallIcon(R.drawable.vpn_connected) 418a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh .setLargeIcon(icon) 419a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh .setContentTitle(title) 420f8905fd13da0bfd6049daebc1cf4f8af286a04deChia-chi Yeh .setContentText(text) 421899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey .setContentIntent(mStatusIntent) 42250fe709995d1f126e96cafde133bc4777b31d4edChia-chi Yeh .setDefaults(0) 423ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh .setOngoing(true) 424899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey .build(); 42550cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackborn nm.notifyAsUser(null, R.drawable.vpn_connected, notification, UserHandle.ALL); 426ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 427ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 428ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 429ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh private void hideNotification() { 43069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey if (!mEnableNotif) return; 431899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mStatusIntent = null; 432899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 433ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh NotificationManager nm = (NotificationManager) 434ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh mContext.getSystemService(Context.NOTIFICATION_SERVICE); 435ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 436ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh if (nm != null) { 43750cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackborn nm.cancelAsUser(null, R.drawable.vpn_connected, UserHandle.ALL); 438ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 439ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh } 440ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh 44197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh private native int jniCreate(int mtu); 442c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh private native String jniGetName(int tun); 44397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh private native int jniSetAddresses(String interfaze, String addresses); 44497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh private native int jniSetRoutes(String interfaze, String routes); 445c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh private native void jniReset(String interfaze); 446c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh private native int jniCheck(String interfaze); 447c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh private native void jniProtect(int socket, String interfaze); 44885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 44982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey private static String findLegacyVpnGateway(LinkProperties prop) { 45082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey for (RouteInfo route : prop.getRoutes()) { 45182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey // Currently legacy VPN only works on IPv4. 45282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) { 45382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey return route.getGateway().getHostAddress(); 45482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 45582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 45682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey 45782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey throw new IllegalStateException("Unable to find suitable gateway"); 45882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 45982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey 46085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh /** 46182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey * Start legacy VPN, controlling native daemons as needed. Creates a 46282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey * secondary thread to perform connection work, returning quickly. 46385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh */ 46482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) { 46582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey if (keyStore.state() != KeyStore.State.UNLOCKED) { 46682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey throw new IllegalStateException("KeyStore isn't unlocked"); 46782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 46882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey 46982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey final String iface = egress.getInterfaceName(); 47082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey final String gateway = findLegacyVpnGateway(egress); 47182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey 47282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey // Load certificates. 47382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey String privateKey = ""; 47482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey String userCert = ""; 47582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey String caCert = ""; 47682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey String serverCert = ""; 47782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey if (!profile.ipsecUserCert.isEmpty()) { 47882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert; 47982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert); 48082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey userCert = (value == null) ? null : new String(value, Charsets.UTF_8); 48182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 48282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey if (!profile.ipsecCaCert.isEmpty()) { 48382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert); 48482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey caCert = (value == null) ? null : new String(value, Charsets.UTF_8); 48582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 48682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey if (!profile.ipsecServerCert.isEmpty()) { 48782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert); 48882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey serverCert = (value == null) ? null : new String(value, Charsets.UTF_8); 48982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 49082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey if (privateKey == null || userCert == null || caCert == null || serverCert == null) { 49182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey throw new IllegalStateException("Cannot load credentials"); 49282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 49382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey 49482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey // Prepare arguments for racoon. 49582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey String[] racoon = null; 49682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey switch (profile.type) { 49782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey case VpnProfile.TYPE_L2TP_IPSEC_PSK: 49882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey racoon = new String[] { 49982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey iface, profile.server, "udppsk", profile.ipsecIdentifier, 50082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey profile.ipsecSecret, "1701", 50182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey }; 50282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey break; 50382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey case VpnProfile.TYPE_L2TP_IPSEC_RSA: 50482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey racoon = new String[] { 50582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey iface, profile.server, "udprsa", privateKey, userCert, 50682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey caCert, serverCert, "1701", 50782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey }; 50882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey break; 50982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey case VpnProfile.TYPE_IPSEC_XAUTH_PSK: 51082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey racoon = new String[] { 51182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey iface, profile.server, "xauthpsk", profile.ipsecIdentifier, 51282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey profile.ipsecSecret, profile.username, profile.password, "", gateway, 51382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey }; 51482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey break; 51582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey case VpnProfile.TYPE_IPSEC_XAUTH_RSA: 51682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey racoon = new String[] { 51782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey iface, profile.server, "xauthrsa", privateKey, userCert, 51882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey caCert, serverCert, profile.username, profile.password, "", gateway, 51982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey }; 52082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey break; 52182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey case VpnProfile.TYPE_IPSEC_HYBRID_RSA: 52282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey racoon = new String[] { 52382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey iface, profile.server, "hybridrsa", 52482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey caCert, serverCert, profile.username, profile.password, "", gateway, 52582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey }; 52682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey break; 52782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 52882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey 52982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey // Prepare arguments for mtpd. 53082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey String[] mtpd = null; 53182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey switch (profile.type) { 53282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey case VpnProfile.TYPE_PPTP: 53382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey mtpd = new String[] { 53482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey iface, "pptp", profile.server, "1723", 53582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey "name", profile.username, "password", profile.password, 53682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey "linkname", "vpn", "refuse-eap", "nodefaultroute", 53782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", 53882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey (profile.mppe ? "+mppe" : "nomppe"), 53982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey }; 54082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey break; 54182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey case VpnProfile.TYPE_L2TP_IPSEC_PSK: 54282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey case VpnProfile.TYPE_L2TP_IPSEC_RSA: 54382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey mtpd = new String[] { 54482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey iface, "l2tp", profile.server, "1701", profile.l2tpSecret, 54582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey "name", profile.username, "password", profile.password, 54682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey "linkname", "vpn", "refuse-eap", "nodefaultroute", 54782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", 54882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey }; 54982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey break; 55082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 551899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 55282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey VpnConfig config = new VpnConfig(); 553899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey config.legacy = true; 55482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey config.user = profile.key; 55582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey config.interfaze = iface; 55682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey config.session = profile.name; 55782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey config.routes = profile.routes; 55882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey if (!profile.dnsServers.isEmpty()) { 55982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey config.dnsServers = Arrays.asList(profile.dnsServers.split(" +")); 56082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 56182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey if (!profile.searchDomains.isEmpty()) { 56282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey config.searchDomains = Arrays.asList(profile.searchDomains.split(" +")); 56382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 56482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey 56582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey startLegacyVpn(config, racoon, mtpd); 56682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey } 56782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey 56882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { 56982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey stopLegacyVpn(); 570899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 571100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh // Prepare for the new request. This also checks the caller. 572100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh prepare(null, VpnConfig.LEGACY_VPN); 573899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.CONNECTING, "startLegacyVpn"); 57485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 5752e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh // Start a new LegacyVpnRunner and we are done! 576100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd); 577100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh mLegacyVpnRunner.start(); 57885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 57985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 580899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey public synchronized void stopLegacyVpn() { 581899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (mLegacyVpnRunner != null) { 582899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mLegacyVpnRunner.exit(); 583899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey mLegacyVpnRunner = null; 584899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 585899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey synchronized (LegacyVpnRunner.TAG) { 586899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // wait for old thread to completely finish before spinning up 587899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // new instance, otherwise state updates can be out of order. 588899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 589899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 590899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 591899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 59285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh /** 5932e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh * Return the information of the current ongoing legacy VPN. 5942e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh */ 5952e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh public synchronized LegacyVpnInfo getLegacyVpnInfo() { 596dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh // Check if the caller is authorized. 597dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh enforceControlPermission(); 598899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (mLegacyVpnRunner == null) return null; 599899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 600899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey final LegacyVpnInfo info = new LegacyVpnInfo(); 601899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey info.key = mLegacyVpnRunner.mConfig.user; 602899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo); 603899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (mNetworkInfo.isConnected()) { 604899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey info.intent = mStatusIntent; 605899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 606899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey return info; 6072e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh } 6082e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh 60969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey public VpnConfig getLegacyVpnConfig() { 61069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey if (mLegacyVpnRunner != null) { 61169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey return mLegacyVpnRunner.mConfig; 61269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey } else { 61369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey return null; 61469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey } 61569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey } 61669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey 6172e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh /** 61885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh * Bringing up a VPN connection takes time, and that is all this thread 61985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh * does. Here we have plenty of time. The only thing we need to take 62085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh * care of is responding to interruptions as soon as possible. Otherwise 62185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh * requests will be piled up. This can be done in a Handler as a state 62285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh * machine, but it is much easier to read in the current form. 62385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh */ 62485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh private class LegacyVpnRunner extends Thread { 62585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh private static final String TAG = "LegacyVpnRunner"; 62685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 62741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh private final VpnConfig mConfig; 6281f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh private final String[] mDaemons; 62985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh private final String[][] mArguments; 6305317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh private final LocalSocket[] mSockets; 63153c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt private final String mOuterInterface; 6322e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh 63385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh private long mTimer = -1; 63485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 63541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) { 63685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh super(TAG); 63741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh mConfig = config; 63841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh mDaemons = new String[] {"racoon", "mtpd"}; 639899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey // TODO: clear arguments from memory once launched 64041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh mArguments = new String[][] {racoon, mtpd}; 6415317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh mSockets = new LocalSocket[mDaemons.length]; 64253c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt 64353c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt // This is the interface which VPN is running on, 64453c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt // mConfig.interfaze will change to point to OUR 64553c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt // internal interface soon. TODO - add inner/outer to mconfig 64653c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt mOuterInterface = mConfig.interfaze; 64741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh } 64841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh 649aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh public void check(String interfaze) { 65053c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt if (interfaze.equals(mOuterInterface)) { 651aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh Log.i(TAG, "Legacy VPN is going down with " + interfaze); 652aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh exit(); 653aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh } 654aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh } 655aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh 65641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh public void exit() { 6575317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh // We assume that everything is reset after stopping the daemons. 65897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh interrupt(); 6595317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh for (LocalSocket socket : mSockets) { 660065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey IoUtils.closeQuietly(socket); 66141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh } 662899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.DISCONNECTED, "exit"); 6632e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh } 6642e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh 66585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh @Override 66685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh public void run() { 66785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh // Wait for the previous thread since it has been interrupted. 6682e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh Log.v(TAG, "Waiting"); 66985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh synchronized (TAG) { 6702e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh Log.v(TAG, "Executing"); 67185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh execute(); 672899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey monitorDaemons(); 67385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 67485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 67585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 67685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh private void checkpoint(boolean yield) throws InterruptedException { 67785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh long now = SystemClock.elapsedRealtime(); 67885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh if (mTimer == -1) { 67985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh mTimer = now; 68085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh Thread.sleep(1); 6817ef8611b5f3a893a46c7b9e22bdd8ab252e373ffChia-chi Yeh } else if (now - mTimer <= 60000) { 68285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh Thread.sleep(yield ? 200 : 1); 68385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } else { 684899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.FAILED, "checkpoint"); 68597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh throw new IllegalStateException("Time is up"); 68685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 68785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 68885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 68985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh private void execute() { 69085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh // Catch all exceptions so we can clean up few things. 691899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey boolean initFinished = false; 69285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh try { 69385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh // Initialize the timer. 69485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh checkpoint(false); 69585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 6961f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh // Wait for the daemons to stop. 6971f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh for (String daemon : mDaemons) { 698088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey while (!SystemService.isStopped(daemon)) { 69985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh checkpoint(true); 70085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 70185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 70285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 70397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Clear the previous state. 70497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh File state = new File("/data/misc/vpn/state"); 70597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh state.delete(); 70697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh if (state.exists()) { 70797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh throw new IllegalStateException("Cannot delete the state"); 70885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 709c1872732922214de80f790e14865e41dd1b98203Chia-chi Yeh new File("/data/misc/vpn/abort").delete(); 710899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey initFinished = true; 71185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 712e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh // Check if we need to restart any of the daemons. 71385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh boolean restart = false; 71485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh for (String[] arguments : mArguments) { 71585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh restart = restart || (arguments != null); 71685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 71785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh if (!restart) { 718899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.DISCONNECTED, "execute"); 71985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh return; 72085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 721899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.CONNECTING, "execute"); 72285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 7231f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh // Start the daemon with arguments. 7241f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh for (int i = 0; i < mDaemons.length; ++i) { 72585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh String[] arguments = mArguments[i]; 72685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh if (arguments == null) { 72785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh continue; 72885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 72985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 7301f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh // Start the daemon. 7311f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh String daemon = mDaemons[i]; 732088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey SystemService.start(daemon); 73385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 7341f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh // Wait for the daemon to start. 735088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey while (!SystemService.isRunning(daemon)) { 73685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh checkpoint(true); 73785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 73885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 73985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh // Create the control socket. 7405317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh mSockets[i] = new LocalSocket(); 74185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh LocalSocketAddress address = new LocalSocketAddress( 7421f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh daemon, LocalSocketAddress.Namespace.RESERVED); 74385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 74485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh // Wait for the socket to connect. 74585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh while (true) { 74685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh try { 7475317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh mSockets[i].connect(address); 74885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh break; 74985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } catch (Exception e) { 75085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh // ignore 75185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 75285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh checkpoint(true); 75385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 7545317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh mSockets[i].setSoTimeout(500); 75585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 75685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh // Send over the arguments. 7575317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh OutputStream out = mSockets[i].getOutputStream(); 75885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh for (String argument : arguments) { 75985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh byte[] bytes = argument.getBytes(Charsets.UTF_8); 7605317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh if (bytes.length >= 0xFFFF) { 76197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh throw new IllegalArgumentException("Argument is too large"); 76285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 7631f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh out.write(bytes.length >> 8); 7641f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh out.write(bytes.length); 7651f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh out.write(bytes); 76685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh checkpoint(false); 76785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 7685317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh out.write(0xFF); 7695317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh out.write(0xFF); 7701f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh out.flush(); 77197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh 77297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Wait for End-of-File. 7735317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh InputStream in = mSockets[i].getInputStream(); 77497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh while (true) { 77597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh try { 77697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh if (in.read() == -1) { 77797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh break; 77897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 77997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } catch (Exception e) { 78097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // ignore 78197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 78297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh checkpoint(true); 78397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 78485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 78585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 78697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Wait for the daemons to create the new state. 78797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh while (!state.exists()) { 7881f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh // Check if a running daemon is dead. 7891f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh for (int i = 0; i < mDaemons.length; ++i) { 7901f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh String daemon = mDaemons[i]; 791088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey if (mArguments[i] != null && !SystemService.isRunning(daemon)) { 7922e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh throw new IllegalStateException(daemon + " is dead"); 79385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 79485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 79585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh checkpoint(true); 79685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 79785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 79897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Now we are connected. Read and parse the new state. 799c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yeh String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1); 80097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh if (parameters.length != 6) { 80197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh throw new IllegalStateException("Cannot parse the state"); 80297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 80397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh 80497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Set the interface and the addresses in the config. 80597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh mConfig.interfaze = parameters[0].trim(); 80697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh mConfig.addresses = parameters[1].trim(); 80785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh 80897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Set the routes if they are not set in the config. 80997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh if (mConfig.routes == null || mConfig.routes.isEmpty()) { 81097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh mConfig.routes = parameters[2].trim(); 81197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 81297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh 81397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Set the DNS servers if they are not set in the config. 81441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) { 81597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh String dnsServers = parameters[3].trim(); 81641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh if (!dnsServers.isEmpty()) { 81741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh mConfig.dnsServers = Arrays.asList(dnsServers.split(" ")); 81841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh } 81941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh } 82041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh 82197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Set the search domains if they are not set in the config. 82297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) { 82397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh String searchDomains = parameters[4].trim(); 82497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh if (!searchDomains.isEmpty()) { 82597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh mConfig.searchDomains = Arrays.asList(searchDomains.split(" ")); 82697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 82797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh } 82897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh 82997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Set the routes. 83097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh jniSetRoutes(mConfig.interfaze, mConfig.routes); 831e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh 83297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh // Here is the last step and it must be done synchronously. 83341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh synchronized (Vpn.this) { 83441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh // Check if the thread is interrupted while we are waiting. 83541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh checkpoint(false); 83641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh 837e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh // Check if the interface is gone while we are waiting. 838c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh if (jniCheck(mConfig.interfaze) == 0) { 83934e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh throw new IllegalStateException(mConfig.interfaze + " is gone"); 84041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh } 841e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh 842e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh // Now INetworkManagementEventObserver is watching our back. 843c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh mInterface = mConfig.interfaze; 84441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh mCallback.override(mConfig.dnsServers, mConfig.searchDomains); 84541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh showNotification(mConfig, null, null); 8462e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh 8472e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh Log.i(TAG, "Connected!"); 848899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.CONNECTED, "execute"); 84941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh } 85085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } catch (Exception e) { 8512e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh Log.i(TAG, "Aborting", e); 852e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh exit(); 8532e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh } finally { 8545317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh // Kill the daemons if they fail to stop. 855899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (!initFinished) { 8565317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh for (String daemon : mDaemons) { 857088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey SystemService.stop(daemon); 8585317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh } 8595317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh } 8605317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh 8612e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh // Do not leave an unstable state. 862899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) { 863899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.FAILED, "execute"); 8642e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh } 86585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 86685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 867899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 868899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey /** 869899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey * Monitor the daemons we started, moving to disconnected state if the 870899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey * underlying services fail. 871899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey */ 872899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey private void monitorDaemons() { 873899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (!mNetworkInfo.isConnected()) { 874899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey return; 875899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 876899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 877899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey try { 878899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey while (true) { 879899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey Thread.sleep(2000); 880899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey for (int i = 0; i < mDaemons.length; i++) { 881899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) { 882899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey return; 883899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 884899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 885899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 886899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } catch (InterruptedException e) { 887899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey Log.d(TAG, "interrupted during monitorDaemons(); stopping services"); 888899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } finally { 889899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey for (String daemon : mDaemons) { 890899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey SystemService.stop(daemon); 891899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 892899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey 893899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey updateState(DetailedState.DISCONNECTED, "babysit"); 894899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 895899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey } 89685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh } 897ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh} 898