Rewrite all the sandbox strategy implementations

- Make use of existing abstractions like SpawnRunner and SpawnExecutionPolicy.
- Instead of having the *Strategy create a *Runner, and then call back into
  SandboxStrategy, create a single SandboxContainer which contains the full
  command line, environment, and everything needed to create and delete the
  sandbox directory.
- Do all the work in SandboxStrategy, including creation and deletion of the
  sandbox directory.
- Use SpawnResult instead of throwing, catching, and rethrowing.
- Simplify the control flow a bit.

PiperOrigin-RevId: 161644979
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java
index e7d605f..e26f37b 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java
@@ -19,16 +19,18 @@
 import com.google.devtools.build.lib.actions.ActionExecutionContext;
 import com.google.devtools.build.lib.actions.ActionInput;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
 import com.google.devtools.build.lib.actions.ExecException;
 import com.google.devtools.build.lib.actions.Spawn;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
-import com.google.devtools.build.lib.buildtool.BuildRequest;
 import com.google.devtools.build.lib.exec.SpawnInputExpander;
+import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy;
 import com.google.devtools.build.lib.rules.fileset.FilesetActionContext;
 import com.google.devtools.build.lib.standalone.StandaloneSpawnStrategy;
 import com.google.devtools.build.lib.util.Preconditions;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.common.options.OptionsProvider;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -47,12 +49,6 @@
     standaloneStrategy.exec(spawn, actionExecutionContext);
   }
 
-  static void reportSubcommand(ActionExecutionContext actionExecutionContext, Spawn spawn) {
-    if (actionExecutionContext.reportsSubcommands()) {
-      actionExecutionContext.reportSubcommand(spawn);
-    }
-  }
-
   /**
    * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the
    * host filesystem where the input files can be found.
@@ -69,6 +65,30 @@
             executionContext.getArtifactExpander(),
             executionContext.getActionInputFileCache(),
             executionContext.getContext(FilesetActionContext.class));
+    return postProcess(inputMap, spawn, executionContext.getArtifactExpander(), execRoot);
+  }
+
+  /**
+   * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the
+   * host filesystem where the input files can be found.
+   */
+  public static Map<PathFragment, Path> getInputFiles(
+      Spawn spawn,
+      SpawnExecutionPolicy policy,
+      Path execRoot)
+          throws IOException {
+    return postProcess(policy.getInputMapping(), spawn, policy.getArtifactExpander(), execRoot);
+  }
+
+  /**
+   * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the
+   * host filesystem where the input files can be found.
+   */
+  private static Map<PathFragment, Path> postProcess(
+      Map<PathFragment, ActionInput> inputMap,
+      Spawn spawn, 
+      ArtifactExpander artifactExpander,
+      Path execRoot) {
     // SpawnInputExpander#getInputMapping uses ArtifactExpander#expandArtifacts to expand
     // middlemen and tree artifacts, which expands empty tree artifacts to no entry. However,
     // actions that accept TreeArtifacts as inputs generally expect that the empty directory is
@@ -77,7 +97,7 @@
     for (ActionInput input : spawn.getInputFiles()) {
       if (input instanceof Artifact && ((Artifact) input).isTreeArtifact()) {
         List<Artifact> containedArtifacts = new ArrayList<>();
-        executionContext.getArtifactExpander().expand((Artifact) input, containedArtifacts);
+        artifactExpander.expand((Artifact) input, containedArtifacts);
         // Attempting to mount a non-empty directory results in ERR_DIRECTORY_NOT_EMPTY, so we
         // only mount empty TreeArtifacts as directories.
         if (containedArtifacts.isEmpty()) {
@@ -105,17 +125,24 @@
     return outputFiles.build();
   }
 
-  static boolean shouldAllowNetwork(BuildRequest buildRequest, Spawn spawn) {
+  /**
+   * Returns true if the build options are set in a way that requires network access for all
+   * actions. This is separate from {@link #shouldAllowNetwork(Spawn)} to avoid having to keep a
+   * reference to the full set of build options (and also for performance, since this only needs to
+   * be checked once-per-build).
+   */
+  static boolean shouldAllowNetwork(OptionsProvider buildOptions) {
     // Allow network access, when --java_debug is specified, otherwise we can't connect to the
     // remote debug server of the test. This intentionally overrides the "block-network" execution
     // tag.
-    if (buildRequest
+    return buildOptions
         .getOptions(BuildConfiguration.Options.class)
         .testArguments
-        .contains("--wrapper_script_flag=--debug")) {
-      return true;
-    }
+        .contains("--wrapper_script_flag=--debug");
+  }
 
+  /** Returns true if this specific spawn requires network access. */
+  static boolean shouldAllowNetwork(Spawn spawn) {
     // If the Spawn requests to block network access, do so.
     if (spawn.getExecutionInfo().containsKey("block-network")) {
       return false;