public void addRoute(String dest, String mask, String gateway, String device) { CIDRIP route = new CIDRIP(dest, mask); boolean include = isAndroidTunDevice(device); NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32), false); if (mLocalIP == null) { VpnStatus.logError( "Local IP address unset but adding route?! This is broken! Please contact author with log"); return; } NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP, true); if (localNet.containsNet(gatewayIP)) include = true; if (gateway != null && (gateway.equals("255.255.255.255") || gateway.equals(mRemoteGW))) include = true; if (route.len == 32 && !mask.equals("255.255.255.255")) { VpnStatus.logWarning(R.string.route_not_cidr, dest, mask); } if (route.normalise()) VpnStatus.logWarning(R.string.route_not_netip, dest, route.len, route.mIp); mRoutes.addIP(route, include); }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setAllowedVpnPackages(Builder builder) { boolean atLeastOneAllowedApp = false; for (String pkg : mProfile.mAllowedAppsVpn) { try { if (mProfile.mAllowedAppsVpnAreDisallowed) { builder.addDisallowedApplication(pkg); } else { builder.addAllowedApplication(pkg); atLeastOneAllowedApp = true; } } catch (PackageManager.NameNotFoundException e) { mProfile.mAllowedAppsVpn.remove(pkg); VpnStatus.logInfo(R.string.app_no_longer_exists, pkg); } } if (!mProfile.mAllowedAppsVpnAreDisallowed && !atLeastOneAllowedApp) { VpnStatus.logDebug(R.string.no_allowed_app, getPackageName()); try { builder.addAllowedApplication(getPackageName()); } catch (PackageManager.NameNotFoundException e) { VpnStatus.logError("This should not happen: " + e.getLocalizedMessage()); } } if (mProfile.mAllowedAppsVpnAreDisallowed) { VpnStatus.logDebug( R.string.disallowed_vpn_apps_info, TextUtils.join(", ", mProfile.mAllowedAppsVpn)); } else { VpnStatus.logDebug( R.string.allowed_vpn_apps_info, TextUtils.join(", ", mProfile.mAllowedAppsVpn)); } }
@Override public void run() { try { Log.i(TAG, "Starting openvpn"); startOpenVPNThreadArgs(mArgv, mProcessEnv); Log.i(TAG, "Giving up"); } catch (Exception e) { VpnStatus.logException("Starting OpenVPN Thread", e); Log.e(TAG, "OpenVPNThread Got " + e.toString()); } finally { int exitvalue = 0; try { if (mProcess != null) exitvalue = mProcess.waitFor(); } catch (IllegalThreadStateException ite) { VpnStatus.logError("Illegal Thread state: " + ite.getLocalizedMessage()); } catch (InterruptedException ie) { VpnStatus.logError("InterruptedException: " + ie.getLocalizedMessage()); } if (exitvalue != 0) { VpnStatus.logError("Process exited with exit value " + exitvalue); if (mBrokenPie) { /* This will probably fail since the NoPIE binary is probably not written */ String[] noPieArgv = VPNLaunchHelper.replacePieWithNoPie(mArgv); // We are already noPIE, nothing to gain if (!noPieArgv.equals(mArgv)) { mArgv = noPieArgv; VpnStatus.logInfo("PIE Version could not be executed. Trying no PIE version"); run(); return; } } } VpnStatus.updateStateString( "NOPROCESS", "No process running.", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED); if (mDumpPath != null) { try { BufferedWriter logout = new BufferedWriter(new FileWriter(mDumpPath + ".log")); SimpleDateFormat timeformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.GERMAN); for (LogItem li : VpnStatus.getlogbuffer()) { String time = timeformat.format(new Date(li.getLogtime())); logout.write(time + " " + li.getString(mService) + "\n"); } logout.close(); VpnStatus.logError(R.string.minidump_generated); } catch (IOException e) { VpnStatus.logError("Writing minidump log: " + e.getLocalizedMessage()); } } mService.processDied(); Log.i(TAG, "Exiting"); } }
private void endVpnService() { synchronized (mProcessLock) { mProcessThread = null; } VpnStatus.removeByteCountListener(this); unregisterDeviceStateReceiver(); ProfileManager.setConntectedVpnProfileDisconnected(this); if (!mStarting) { stopForeground(!mNotificationAlwaysVisible); if (!mNotificationAlwaysVisible) { stopSelf(); VpnStatus.removeStateListener(this); } } }
@Override public void updateState(String state, String logmessage, int resid, ConnectionStatus level) { // If the process is not running, ignore any state, // Notification should be invisible in this state doSendBroadcast(state, level); if (mProcessThread == null && !mNotificationAlwaysVisible) return; boolean lowpriority = false; // Display byte count only after being connected { if (level == LEVEL_WAITING_FOR_USER_INPUT) { // The user is presented a dialog of some kind, no need to inform the user // with a notifcation return; } else if (level == LEVEL_CONNECTED) { mDisplayBytecount = true; mConnecttime = System.currentTimeMillis(); lowpriority = true; } else { mDisplayBytecount = false; } // Other notifications are shown, // This also mean we are no longer connected, ignore bytecount messages until next // CONNECTED // Does not work :( String msg = getString(resid); showNotification( msg + " " + VpnStatus.getCleanLogMessage(level, logmessage), msg, lowpriority, 0, level); } }
private void addLocalNetworksToRoutes() { // Add local network interfaces String[] localRoutes = NativeUtils.getIfconfig(); // The format of mLocalRoutes is kind of broken because I don't really like JNI for (int i = 0; i < localRoutes.length; i += 3) { String intf = localRoutes[i]; String ipAddr = localRoutes[i + 1]; String netMask = localRoutes[i + 2]; if (intf == null || intf.equals("lo") || intf.startsWith("tun") || intf.startsWith("rmnet")) continue; if (ipAddr == null || netMask == null) { VpnStatus.logError( "Local routes are broken?! (Report to author) " + TextUtils.join("|", localRoutes)); continue; } if (ipAddr.equals(mLocalIP.mIp)) continue; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && !mProfile.mAllowLocalLAN) { mRoutes.addIPSplit(new CIDRIP(ipAddr, netMask), true); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mProfile.mAllowLocalLAN) mRoutes.addIP(new CIDRIP(ipAddr, netMask), false); } }
public void setLocalIP(String local, String netmask, int mtu, String mode) { mLocalIP = new CIDRIP(local, netmask); mMtu = mtu; mRemoteGW = null; long netMaskAsInt = CIDRIP.getInt(netmask); if (mLocalIP.len == 32 && !netmask.equals("255.255.255.255")) { // get the netmask as IP int masklen; long mask; if ("net30".equals(mode)) { masklen = 30; mask = 0xfffffffc; } else { masklen = 31; mask = 0xfffffffe; } // Netmask is Ip address +/-1, assume net30/p2p with small net if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask)) { mLocalIP.len = masklen; } else { mLocalIP.len = 32; if (!"p2p".equals(mode)) VpnStatus.logWarning(R.string.ip_not_cidr, local, netmask, mode); } } if (("p2p".equals(mode) && mLocalIP.len < 32) || ("net30".equals(mode) && mLocalIP.len < 30)) { VpnStatus.logWarning(R.string.ip_looks_like_subnet, local, netmask, mode); } /* Workaround for Lollipop, it does not route traffic to the VPNs own network mask */ if (mLocalIP.len <= 31 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CIDRIP interfaceRoute = new CIDRIP(mLocalIP.mIp, mLocalIP.len); interfaceRoute.normalise(); addRoute(interfaceRoute); } // Configurations are sometimes really broken... mRemoteGW = netmask; }
public static String getCertificateFriendlyName(X509Certificate cert) { X500Principal principal = cert.getSubjectX500Principal(); byte[] encodedSubject = principal.getEncoded(); String friendlyName = null; /* Hack so we do not have to ship a whole Spongy/bouncycastle */ Exception exp = null; try { Class X509NameClass = Class.forName("com.android.org.bouncycastle.asn1.x509.X509Name"); Method getInstance = X509NameClass.getMethod("getInstance", Object.class); Hashtable defaultSymbols = (Hashtable) X509NameClass.getField("DefaultSymbols").get(X509NameClass); if (!defaultSymbols.containsKey("1.2.840.113549.1.9.1")) defaultSymbols.put("1.2.840.113549.1.9.1", "eMail"); Object subjectName = getInstance.invoke(X509NameClass, encodedSubject); Method toString = X509NameClass.getMethod("toString", boolean.class, Hashtable.class); friendlyName = (String) toString.invoke(subjectName, true, defaultSymbols); } catch (ClassNotFoundException e) { exp = e; } catch (NoSuchMethodException e) { exp = e; } catch (InvocationTargetException e) { exp = e; } catch (IllegalAccessException e) { exp = e; } catch (NoSuchFieldException e) { exp = e; } if (exp != null) VpnStatus.logException("Getting X509 Name from certificate", exp); /* Fallback if the reflection method did not work */ if (friendlyName == null) friendlyName = principal.getName(); // Really evil hack to decode email address // See: http://code.google.com/p/android/issues/detail?id=21531 String[] parts = friendlyName.split(","); for (int i = 0; i < parts.length; i++) { String part = parts[i]; if (part.startsWith("1.2.840.113549.1.9.1=#16")) { parts[i] = "email=" + ia5decode(part.replace("1.2.840.113549.1.9.1=#16", "")); } } friendlyName = TextUtils.join(",", parts); return friendlyName; }
synchronized void registerDeviceStateReceiver(OpenVPNManagement magnagement) { // Registers BroadcastReceiver to track network connection changes. IntentFilter filter = new IntentFilter(); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); mDeviceStateReceiver = new DeviceStateReceiver(magnagement); registerReceiver(mDeviceStateReceiver, filter); VpnStatus.addByteCountListener(mDeviceStateReceiver); /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) addLollipopCMListener(); */ }
public static String getCertificateFriendlyName(Context c, String filename) { if (!TextUtils.isEmpty(filename)) { try { X509Certificate cert = (X509Certificate) getCertificateFromFile(filename); String friendlycn = getCertificateFriendlyName(cert); friendlycn = getCertificateValidityString(cert, c.getResources()) + friendlycn; return friendlycn; } catch (Exception e) { VpnStatus.logError("Could not read certificate" + e.getLocalizedMessage()); } } return c.getString(R.string.cannotparsecert); }
@Override public void onDestroy() { synchronized (mProcessLock) { if (mProcessThread != null) { mManagement.stopVPN(); } } if (mDeviceStateReceiver != null) { this.unregisterReceiver(mDeviceStateReceiver); } // Just in case unregister for state VpnStatus.removeStateListener(this); }
public void addRoutev6(String network, String device) { String[] v6parts = network.split("/"); boolean included = isAndroidTunDevice(device); // Tun is opened after ROUTE6, no device name may be present try { Inet6Address ip = (Inet6Address) InetAddress.getAllByName(v6parts[0])[0]; int mask = Integer.parseInt(v6parts[1]); mRoutesv6.addIPv6(ip, mask, included); } catch (UnknownHostException e) { VpnStatus.logException(e); } }
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void jbNotificationExtras( boolean lowpriority, android.app.Notification.Builder nbuilder) { try { if (lowpriority) { Method setpriority = nbuilder.getClass().getMethod("setPriority", int.class); // PRIORITY_MIN == -2 setpriority.invoke(nbuilder, -2); Method setUsesChronometer = nbuilder.getClass().getMethod("setUsesChronometer", boolean.class); setUsesChronometer.invoke(nbuilder, true); } Intent disconnectVPN = new Intent(this, DisconnectVPN.class); disconnectVPN.setAction(DISCONNECT_VPN); PendingIntent disconnectPendingIntent = PendingIntent.getActivity(this, 0, disconnectVPN, 0); nbuilder.addAction( android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.cancel_connection), disconnectPendingIntent); Intent pauseVPN = new Intent(this, OpenVPNService.class); if (mDeviceStateReceiver == null || !mDeviceStateReceiver.isUserPaused()) { pauseVPN.setAction(PAUSE_VPN); PendingIntent pauseVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0); nbuilder.addAction( android.R.drawable.ic_media_pause, getString(R.string.pauseVPN), pauseVPNPending); } else { pauseVPN.setAction(RESUME_VPN); PendingIntent resumeVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0); nbuilder.addAction( android.R.drawable.ic_media_play, getString(R.string.resumevpn), resumeVPNPending); } // ignore exception } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { VpnStatus.logException(e); } }
synchronized void unregisterDeviceStateReceiver() { if (mDeviceStateReceiver != null) try { VpnStatus.removeByteCountListener(mDeviceStateReceiver); this.unregisterReceiver(mDeviceStateReceiver); } catch (IllegalArgumentException iae) { // I don't know why this happens: // java.lang.IllegalArgumentException: Receiver not registered: // de.blinkt.openvpn.NetworkSateReceiver@41a61a10 // Ignore for now ... iae.printStackTrace(); } mDeviceStateReceiver = null; /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) removeLollipopCMListener();*/ }
public ParcelFileDescriptor openTun() { // Debug.startMethodTracing(getExternalFilesDir(null).toString() + "/opentun.trace", 40* 1024 * // 1024); Builder builder = new Builder(); VpnStatus.logInfo(R.string.last_openvpn_tun_config); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mProfile.mAllowLocalLAN) { allowAllAFFamilies(builder); } if (mLocalIP == null && mLocalIPv6 == null) { VpnStatus.logError(getString(R.string.opentun_no_ipaddr)); return null; } if (mLocalIP != null) { addLocalNetworksToRoutes(); try { builder.addAddress(mLocalIP.mIp, mLocalIP.len); } catch (IllegalArgumentException iae) { VpnStatus.logError(R.string.dns_add_error, mLocalIP, iae.getLocalizedMessage()); return null; } } if (mLocalIPv6 != null) { String[] ipv6parts = mLocalIPv6.split("/"); try { builder.addAddress(ipv6parts[0], Integer.parseInt(ipv6parts[1])); } catch (IllegalArgumentException iae) { VpnStatus.logError(R.string.ip_add_error, mLocalIPv6, iae.getLocalizedMessage()); return null; } } for (String dns : mDnslist) { try { builder.addDnsServer(dns); } catch (IllegalArgumentException iae) { VpnStatus.logError(R.string.dns_add_error, dns, iae.getLocalizedMessage()); } } String release = Build.VERSION.RELEASE; if ((Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3") && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6")) && mMtu < 1280) { VpnStatus.logInfo( String.format( Locale.US, "Forcing MTU to 1280 instead of %d to workaround Android Bug #70916", mMtu)); builder.setMtu(1280); } else { builder.setMtu(mMtu); } Collection<ipAddress> positiveIPv4Routes = mRoutes.getPositiveIPList(); Collection<ipAddress> positiveIPv6Routes = mRoutesv6.getPositiveIPList(); ipAddress multicastRange = new ipAddress(new CIDRIP("224.0.0.0", 3), true); for (NetworkSpace.ipAddress route : positiveIPv4Routes) { try { if (multicastRange.containsNet(route)) VpnStatus.logDebug(R.string.ignore_multicast_route, route.toString()); else builder.addRoute(route.getIPv4Address(), route.networkMask); } catch (IllegalArgumentException ia) { VpnStatus.logError( getString(R.string.route_rejected) + route + " " + ia.getLocalizedMessage()); } } for (NetworkSpace.ipAddress route6 : positiveIPv6Routes) { try { builder.addRoute(route6.getIPv6Address(), route6.networkMask); } catch (IllegalArgumentException ia) { VpnStatus.logError( getString(R.string.route_rejected) + route6 + " " + ia.getLocalizedMessage()); } } if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mDnslist.size() >= 1) { // Check if the first DNS Server is in the VPN range try { ipAddress dnsServer = new ipAddress(new CIDRIP(mDnslist.get(0), 32), true); boolean dnsIncluded = false; for (ipAddress net : positiveIPv4Routes) { if (net.containsNet(dnsServer)) { dnsIncluded = true; } } if (!dnsIncluded) { String samsungwarning = String.format( "Warning Samsung Android 5.0+ devices ignore DNS servers outside the VPN range. To enable DNS add a custom route to your DNS Server (%s) or change to a DNS inside your VPN range", mDnslist.get(0)); VpnStatus.logWarning(samsungwarning); } } catch (Exception e) { VpnStatus.logError("Error parsing DNS Server IP: " + mDnslist.get(0)); } } if (mDomain != null) builder.addSearchDomain(mDomain); VpnStatus.logInfo(R.string.local_ip_info, mLocalIP.mIp, mLocalIP.len, mLocalIPv6, mMtu); VpnStatus.logInfo(R.string.dns_server_info, TextUtils.join(", ", mDnslist), mDomain); VpnStatus.logInfo( R.string.routes_info_incl, TextUtils.join(", ", mRoutes.getNetworks(true)), TextUtils.join(", ", mRoutesv6.getNetworks(true))); VpnStatus.logInfo( R.string.routes_info_excl, TextUtils.join(", ", mRoutes.getNetworks(false)), TextUtils.join(", ", mRoutesv6.getNetworks(false))); VpnStatus.logDebug( R.string.routes_debug, TextUtils.join(", ", positiveIPv4Routes), TextUtils.join(", ", positiveIPv6Routes)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { setAllowedVpnPackages(builder); } String session = mProfile.mName; if (mLocalIP != null && mLocalIPv6 != null) session = getString(R.string.session_ipv6string, session, mLocalIP, mLocalIPv6); else if (mLocalIP != null) session = getString(R.string.session_ipv4string, session, mLocalIP); builder.setSession(session); // No DNS Server, log a warning if (mDnslist.size() == 0) VpnStatus.logInfo(R.string.warn_no_dns); mLastTunCfg = getTunConfigString(); // Reset information mDnslist.clear(); mRoutes.clear(); mRoutesv6.clear(); mLocalIP = null; mLocalIPv6 = null; mDomain = null; builder.setConfigureIntent(getLogPendingIntent()); try { // Debug.stopMethodTracing(); ParcelFileDescriptor tun = builder.establish(); if (tun == null) throw new NullPointerException( "Android establish() method returned null (Really broken network configuration?)"); return tun; } catch (Exception e) { VpnStatus.logError(R.string.tun_open_error); VpnStatus.logError(getString(R.string.error) + e.getLocalizedMessage()); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) { VpnStatus.logError(R.string.tun_error_helpful); } return null; } }
private void startOpenVPNThreadArgs(String[] argv, Map<String, String> env) { LinkedList<String> argvlist = new LinkedList<String>(); Collections.addAll(argvlist, argv); ProcessBuilder pb = new ProcessBuilder(argvlist); // Hack O rama String lbpath = genLibraryPath(argv, pb); pb.environment().put("LD_LIBRARY_PATH", lbpath); // Add extra variables for (Entry<String, String> e : env.entrySet()) { pb.environment().put(e.getKey(), e.getValue()); } pb.redirectErrorStream(true); try { mProcess = pb.start(); // openvpn被配置为从stdin接受配置文件 // 写入配置文件 Writer writer = new OutputStreamWriter(mProcess.getOutputStream(), "UTF-8"); VpnProfile startProfile = VPNLaunchHelper.getStartProfile(); String configString = startProfile.getConfigFile(VPNLaunchHelper.getStartContext(), false); writer.write(configString); writer.close(); InputStream in = mProcess.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); while (true) { String logline = br.readLine(); if (logline == null) return; if (logline.startsWith(DUMP_PATH_STRING)) mDumpPath = logline.substring(DUMP_PATH_STRING.length()); if (logline.startsWith(BROKEN_PIE_SUPPORT) || logline.contains(BROKEN_PIE_SUPPORT2)) mBrokenPie = true; // 1380308330.240114 18000002 Send to HTTP proxy: 'X-Online-Host: bla.blabla.com' Pattern p = Pattern.compile("(\\d+).(\\d+) ([0-9a-f])+ (.*)"); Matcher m = p.matcher(logline); if (m.matches()) { int flags = Integer.parseInt(m.group(3), 16); String msg = m.group(4); int logLevel = flags & 0x0F; VpnStatus.LogLevel logStatus = VpnStatus.LogLevel.INFO; if ((flags & M_FATAL) != 0) logStatus = VpnStatus.LogLevel.ERROR; else if ((flags & M_NONFATAL) != 0) logStatus = VpnStatus.LogLevel.WARNING; else if ((flags & M_WARN) != 0) logStatus = VpnStatus.LogLevel.WARNING; else if ((flags & M_DEBUG) != 0) logStatus = VpnStatus.LogLevel.VERBOSE; if (msg.startsWith("MANAGEMENT: CMD")) logLevel = Math.max(4, logLevel); VpnStatus.logMessageOpenVPN(logStatus, logLevel, msg); } else { VpnStatus.logInfo("P:" + logline); } } } catch (IOException e) { VpnStatus.logException("Error reading from output of OpenVPN process", e); stopProcess(); } }
@Override public void onRevoke() { VpnStatus.logInfo(R.string.permission_revoked); mManagement.stopVPN(); endVpnService(); }
@Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null && intent.getBooleanExtra(ALWAYS_SHOW_NOTIFICATION, false)) mNotificationAlwaysVisible = true; VpnStatus.addStateListener(this); VpnStatus.addByteCountListener(this); if (intent != null && PAUSE_VPN.equals(intent.getAction())) { if (mDeviceStateReceiver != null) mDeviceStateReceiver.userPause(true); return START_NOT_STICKY; } if (intent != null && RESUME_VPN.equals(intent.getAction())) { if (mDeviceStateReceiver != null) mDeviceStateReceiver.userPause(false); return START_NOT_STICKY; } if (intent != null && START_SERVICE.equals(intent.getAction())) return START_NOT_STICKY; if (intent != null && START_SERVICE_STICKY.equals(intent.getAction())) { return START_REDELIVER_INTENT; } /* The intent is null when the service has been restarted */ if (intent == null) { mProfile = ProfileManager.getLastConnectedProfile(this, false); VpnStatus.logInfo(R.string.service_restarted); /* Got no profile, just stop */ if (mProfile == null) { Log.d("OpenVPN", "Got no last connected profile on null intent. Stopping"); stopSelf(startId); return START_NOT_STICKY; } /* Do the asynchronous keychain certificate stuff */ mProfile.checkForRestart(this); /* Recreate the intent */ intent = mProfile.getStartServiceIntent(this); } else { String profileUUID = intent.getStringExtra(getPackageName() + ".profileUUID"); mProfile = ProfileManager.get(this, profileUUID); } // Extract information from the intent. String prefix = getPackageName(); String[] argv = intent.getStringArrayExtra(prefix + ".ARGV"); String nativeLibraryDirectory = intent.getStringExtra(prefix + ".nativelib"); String startTitle = getString(R.string.start_vpn_title, mProfile.mName); String startTicker = getString(R.string.start_vpn_ticker, mProfile.mName); showNotification(startTitle, startTicker, false, 0, LEVEL_CONNECTING_NO_SERVER_REPLY_YET); // Set a flag that we are starting a new VPN mStarting = true; // Stop the previous session by interrupting the thread. if (mManagement != null && mManagement.stopVPN()) // an old was asked to exit, wait 1s try { Thread.sleep(1000); } catch (InterruptedException e) { // ignore } synchronized (mProcessLock) { if (mProcessThread != null) { mProcessThread.interrupt(); try { Thread.sleep(1000); } catch (InterruptedException e) { // ignore } } } // An old running VPN should now be exited mStarting = false; // Start a new session by creating a new thread. SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); mOvpn3 = prefs.getBoolean("ovpn3", false); if (!"ovpn3".equals(BuildConfig.FLAVOR)) mOvpn3 = false; // Open the Management Interface if (!mOvpn3) { // start a Thread that handles incoming messages of the managment socket OpenVpnManagementThread ovpnManagementThread = new OpenVpnManagementThread(mProfile, this); if (ovpnManagementThread.openManagementInterface(this)) { Thread mSocketManagerThread = new Thread(ovpnManagementThread, "OpenVPNManagementThread"); mSocketManagerThread.start(); mManagement = ovpnManagementThread; VpnStatus.logInfo("started Socket Thread"); } else { return START_NOT_STICKY; } } Runnable processThread; if (mOvpn3) { OpenVPNManagement mOpenVPN3 = instantiateOpenVPN3Core(); processThread = (Runnable) mOpenVPN3; mManagement = mOpenVPN3; } else { HashMap<String, String> env = new HashMap<>(); processThread = new OpenVPNThread(this, argv, env, nativeLibraryDirectory); } synchronized (mProcessLock) { mProcessThread = new Thread(processThread, "OpenVPNProcessThread"); mProcessThread.start(); } if (mDeviceStateReceiver != null) unregisterDeviceStateReceiver(); registerDeviceStateReceiver(mManagement); ProfileManager.setConnectedVpnProfile(this, mProfile); /* TODO: At the moment we have no way to handle asynchronous PW input * Fixing will also allow to handle challenge/response authentication */ if (mProfile.needUserPWInput(true) != 0) return START_NOT_STICKY; return START_STICKY; }