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 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; }