@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 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();
    }
  }