content: package com.termux.x11.controller.core;
  
  import android.os.Process;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.lang.reflect.Field;
  import java.util.ArrayList;
  import java.util.concurrent.Executors;
  
  public abstract class ProcessHelper {
      public static final boolean PRINT_DEBUG = false; // FIXME change to false
      private static final ArrayList<Callback<String>> debugCallbacks = new ArrayList<>();
      private static final byte SIGCONT = 18;
      private static final byte SIGSTOP = 19;
  
      public static void suspendProcess(int pid) {
          Process.sendSignal(pid, SIGSTOP);
      }
  
      public static void resumeProcess(int pid) {
          Process.sendSignal(pid, SIGCONT);
      }
  
      public static int exec(String command) {
          return exec(command, null);
      }
  
      public static int exec(String command, String[] envp) {
          return exec(command, envp, null);
      }
  
      public static int exec(String command, String[] envp, File workingDir) {
          return exec(command, envp, workingDir, null);
      }
  
      public static int exec(String command, String[] envp, File workingDir, Callback<Integer> terminationCallback) {
          int pid = -1;
          try {
              java.lang.Process process = Runtime.getRuntime().exec(splitCommand(command), envp, workingDir);
              Field pidField = process.getClass().getDeclaredField("pid");
              pidField.setAccessible(true);
              pid = pidField.getInt(process);
              pidField.setAccessible(false);
  
              if (!debugCallbacks.isEmpty()) {
                  createDebugThread(process.getInputStream());
                  createDebugThread(process.getErrorStream());
              }
  
              if (terminationCallback != null) createWaitForThread(process, terminationCallback);
          }
          catch (Exception e) {}
          return pid;
      }
  
      private static void createDebugThread(final InputStream inputStream) {
          Executors.newSingleThreadExecutor().execute(() -> {
              try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
                  String line;
                  while ((line = reader.readLine()) != null) {
                      if (PRINT_DEBUG) System.out.println(line);
                      synchronized (debugCallbacks) {
                          if (!debugCallbacks.isEmpty()) {
                              for (Callback<String> callback : debugCallbacks) callback.call(line);
                          }
                      }
                  }
              }
              catch (IOException e) {}
          });
      }
  
      private static void createWaitForThread(java.lang.Process process, final Callback<Integer> terminationCallback) {
          Executors.newSingleThreadExecutor().execute(() -> {
              try {
                  int status = process.waitFor();
                  terminationCallback.call(status);
              }
              catch (InterruptedException e) {}
          });
      }
  
      public static void removeAllDebugCallbacks() {
          synchronized (debugCallbacks) {
              debugCallbacks.clear();
          }
      }
  
      public static void addDebugCallback(Callback<String> callback) {
          synchronized (debugCallbacks) {
              if (!debugCallbacks.contains(callback)) debugCallbacks.add(callback);
          }
      }
  
      public static void removeDebugCallback(Callback<String> callback) {
          synchronized (debugCallbacks) {
              debugCallbacks.remove(callback);
          }
      }
  
      public static String[] splitCommand(String command) {
          ArrayList<String> result = new ArrayList<>();
          boolean startedQuotes = false;
          String value = "";
          char currChar, nextChar;
          for (int i = 0, count = command.length(); i < count; i++) {
              currChar = command.charAt(i);
  
              if (startedQuotes) {
                  if (currChar == '"') {
                      startedQuotes = false;
                      if (!value.isEmpty()) {
                          value += '"';
                          result.add(value);
                          value = "";
                      }
                  }
                  else value += currChar;
              }
              else if (currChar == '"') {
                  startedQuotes = true;
                  value += '"';
              }
              else {
                  nextChar = i < count-1 ? command.charAt(i+1) : '\0';
                  if (currChar == ' ' || (currChar == '\\' && nextChar == ' ')) {
                      if (currChar == '\\') {
                          value += ' ';
                          i++;
                      }
                      else if (!value.isEmpty()) {
                          result.add(value);
                          value = "";
                      }
                  }
                  else {
                      value += currChar;
                      if (i == count-1) {
                          result.add(value);
                          value = "";
                      }
                  }
              }
          }
  
          return result.toArray(new String[0]);
      }
  
      public static String getAffinityMaskAsHexString(String cpuList) {
          String[] values = cpuList.split(",");
          int affinityMask = 0;
          for (String value : values) {
              byte index = Byte.parseByte(value);
              affinityMask |= (int)Math.pow(2, index);
          }
          return Integer.toHexString(affinityMask);
      }
  
      public static int getAffinityMask(String cpuList) {
          if (cpuList == null || cpuList.isEmpty()) return 0;
          String[] values = cpuList.split(",");
          int affinityMask = 0;
          for (String value : values) {
              byte index = Byte.parseByte(value);
              affinityMask |= (int)Math.pow(2, index);
          }
          return affinityMask;
      }
  
      public static int getAffinityMask(boolean[] cpuList) {
          int affinityMask = 0;
          for (int i = 0; i < cpuList.length; i++) {
              if (cpuList[i]) affinityMask |= (int)Math.pow(2, i);
          }
          return affinityMask;
      }
  
      public static int getAffinityMask(int from, int to) {
          int affinityMask = 0;
          for (int i = from; i < to; i++) affinityMask |= (int)Math.pow(2, i);
          return affinityMask;
      }
  }
  