Enable running builds offline using nsjail.

Test: development/sandbox/run-without-network.sh \
      ./gradlew --offline --stacktrace collection2:collection2:tasks

Note: the above currently fails because of b/221086214, which is
      great, because that's precisely the kind of bug we want to
      catch.

Bug: 220164491

Change-Id: I79ca531b6cff248c8cdefa7275fc5f55a0469fac
diff --git a/development/sandbox/nsjail.cfg b/development/sandbox/nsjail.cfg
new file mode 100644
index 0000000..53726ef
--- /dev/null
+++ b/development/sandbox/nsjail.cfg
@@ -0,0 +1,162 @@
+name: "androidx-build-sandbox"
+description: "Sandboxed AndroidX Build."
+description: "No network access and a limited access to local host resources."
+
+# All configuration options are described in
+# https://github.com/google/nsjail/blob/master/config.proto
+
+# Heavily based on https://source.corp.google.com/android/tools/treble/build/sandbox/nsjail.cfg
+
+# Run once then exit
+mode: ONCE
+
+# No time limit
+time_limit: 0
+
+# Limits memory usage
+rlimit_as_type: SOFT
+# Maximum size of core dump files
+rlimit_core_type: SOFT
+# Limits use of CPU time
+rlimit_cpu_type: SOFT
+# Maximum file size
+rlimit_fsize_type: SOFT
+# Maximum number of file descriptors opened
+rlimit_nofile_type: SOFT
+# Maximum stack size
+rlimit_stack_type: SOFT
+# Maximum number of threads
+rlimit_nproc_type: SOFT
+
+# Allow terminal control
+# This let's users cancel jobs with CTRL-C
+# without exiting the jail
+skip_setsid: true
+
+# Below are all the host paths that shall be mounted
+# to the sandbox
+
+# Mount proc as read/write.
+mount {
+  dst: "/proc"
+  fstype: "proc"
+  rw: true
+}
+
+# The sandbox User ID was chosen arbitrarily
+uidmap {
+  inside_id: "999999"
+  outside_id: ""
+  count: 1
+}
+
+# The sandbox Group ID was chosen arbitrarily
+gidmap {
+  inside_id: "65534"
+  outside_id: ""
+  count: 1
+}
+
+# By default nsjail does not propagate the environment into the jail. We need
+# the path to be set up. There are a few ways to solve this problem, but to
+# avoid an undocumented dependency we are explicit about the path we inject.
+envar: "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+
+# Some tools in the build toolchain expect a $HOME to be set
+# Point $HOME to /tmp in case the toolchain needs to write something out there
+envar: "HOME=/tmp"
+mount {
+  dst: "/tmp"
+  fstype: "tmpfs"
+  rw: true
+  is_bind: false
+}
+
+# Some tools need /dev/shm to created a named semaphore. Use a new tmpfs to
+# limit access to the external environment.
+mount {
+  dst: "/dev/shm"
+  fstype: "tmpfs"
+  rw: true
+  is_bind: false
+}
+
+# Map the working User ID to a username
+# Some tools like Java need a valid username
+mount {
+  src_content: "nobody:x:999999:65534:nobody:/tmp:/bin/bash"
+  dst: "/etc/passwd"
+  mandatory: false
+}
+
+# Define default group
+mount {
+  src_content: "nogroup::65534:nogroup"
+  dst: "/etc/group"
+  mandatory: false
+}
+
+# Empty mtab file needed for some build scripts that check for images being mounted
+mount {
+  src_content: "\n"
+  dst: "/etc/mtab"
+  mandatory: false
+}
+
+# Explicitly mount required device file nodes
+#
+# This will enable a chroot based NsJail sandbox. A chroot does not provide
+# device file nodes. So just mount the required device file nodes directly
+# from the host.
+#
+# Note that this has no effect in a docker container, since in that case
+# NsJail will just mount the container device nodes. When we use NsJail
+# in a docker container we mount the full file system root. So the container
+# device nodes were already mounted in the NsJail.
+
+# /dev/null is a very commonly used for silencing output
+mount {
+  src: "/dev/null"
+  dst: "/dev/null"
+  rw: true
+  is_bind: true
+}
+
+# UNUSED options
+# These were set in android/tools/treble, but are not useful to us in AndroidX
+
+# Some tools (like llvm-link) look for file descriptors in /dev/fd
+mount {
+  src: "/proc/self/fd"
+  dst: "/dev/fd"
+  is_symlink: true
+  mandatory: false
+}
+
+# /dev/urandom used during the creation of system.img
+mount {
+  src: "/dev/urandom"
+  dst: "/dev/urandom"
+  rw: true
+  is_bind: true
+}
+
+# /dev/random used by test scripts
+mount {
+  src: "/dev/random"
+  dst: "/dev/random"
+  rw: true
+  is_bind: true
+}
+
+# /dev/zero is required to make vendor-qemu.img
+mount {
+  src: "/dev/zero"
+  dst: "/dev/zero"
+  is_bind: true
+}
+
+# The user must mount the source to /src using --bindmount
+# It will be set as the initial working directory
+# cwd: "/src"
+
diff --git a/development/sandbox/run-without-network.sh b/development/sandbox/run-without-network.sh
new file mode 100755
index 0000000..3031d2b
--- /dev/null
+++ b/development/sandbox/run-without-network.sh
@@ -0,0 +1,12 @@
+# find script (expect it is in frameworks/support/development/sandbox)
+SCRIPT_DIR=$(cd "$(dirname $0)" && pwd)
+
+PREBUILTS_DIR=$SCRIPT_DIR/../../../../prebuilts
+
+$PREBUILTS_DIR/build-tools/linux-x86/bin/nsjail \
+    --config=$SCRIPT_DIR/nsjail.cfg \
+    --chroot=/ \
+    --cwd=$(pwd) \
+    --rw \
+    -- \
+    /bin/bash -c "echo EXECUTING OFFLINE: $* && $*"