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