Revert "Sandbox soong_build by changing to root directory"

This reverts commit 05c25ccb4adb5329add700b533416c226cdbfa96.

Reason for revert: broke absolute OUT_DIR
Bug: 146437378

Change-Id: I523ed79d40e1c1ef040212ba794a7a084abea75d
diff --git a/Android.bp b/Android.bp
index 0382ee2..9403b26 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,7 +69,6 @@
         "android/proto.go",
         "android/register.go",
         "android/rule_builder.go",
-        "android/sandbox.go",
         "android/sdk.go",
         "android/sh_binary.go",
         "android/singleton.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index dbf3aa8..f3c15e4 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -323,7 +323,7 @@
 		return
 	}
 
-	err := translateAndroidMk(ctx, absolutePath(transMk.String()), androidMkModulesList)
+	err := translateAndroidMk(ctx, transMk.String(), androidMkModulesList)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
@@ -364,8 +364,8 @@
 	}
 
 	// Don't write to the file if it hasn't changed
-	if _, err := os.Stat(absolutePath(mkFile)); !os.IsNotExist(err) {
-		if data, err := ioutil.ReadFile(absolutePath(mkFile)); err == nil {
+	if _, err := os.Stat(mkFile); !os.IsNotExist(err) {
+		if data, err := ioutil.ReadFile(mkFile); err == nil {
 			matches := buf.Len() == len(data)
 
 			if matches {
@@ -383,7 +383,7 @@
 		}
 	}
 
-	return ioutil.WriteFile(absolutePath(mkFile), buf.Bytes(), 0666)
+	return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
 }
 
 func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
diff --git a/android/config.go b/android/config.go
index 3c49c1a..101f457 100644
--- a/android/config.go
+++ b/android/config.go
@@ -135,12 +135,12 @@
 }
 
 func loadConfig(config *config) error {
-	err := loadFromConfigFile(&config.FileConfigurableOptions, absolutePath(config.ConfigFileName))
+	err := loadFromConfigFile(&config.FileConfigurableOptions, config.ConfigFileName)
 	if err != nil {
 		return err
 	}
 
-	return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
+	return loadFromConfigFile(&config.productVariables, config.ProductVariablesFileName)
 }
 
 // loads configuration options from a JSON file in the cwd.
@@ -204,17 +204,6 @@
 	return nil
 }
 
-// NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
-// use the android package.
-func NullConfig(buildDir string) Config {
-	return Config{
-		config: &config{
-			buildDir: buildDir,
-			fs:       pathtools.OsFs,
-		},
-	}
-}
-
 // TestConfig returns a Config object suitable for using for tests
 func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
 	envCopy := make(map[string]string)
@@ -331,7 +320,7 @@
 		buildDir:          buildDir,
 		multilibConflicts: make(map[ArchType]bool),
 
-		fs: pathtools.NewOsFs(absSrcDir),
+		fs: pathtools.OsFs,
 	}
 
 	config.deviceConfig = &deviceConfig{
@@ -361,7 +350,7 @@
 	}
 
 	inMakeFile := filepath.Join(buildDir, ".soong.in_make")
-	if _, err := os.Stat(absolutePath(inMakeFile)); err == nil {
+	if _, err := os.Stat(inMakeFile); err == nil {
 		config.inMake = true
 	}
 
@@ -409,8 +398,6 @@
 	return Config{config}, nil
 }
 
-var TestConfigOsFs = map[string][]byte{}
-
 // mockFileSystem replaces all reads with accesses to the provided map of
 // filenames to contents stored as a byte slice.
 func (c *config) mockFileSystem(bp string, fs map[string][]byte) {
@@ -914,13 +901,8 @@
 	return c.productVariables.BootJars
 }
 
-func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
-	if c.productVariables.DexpreoptGlobalConfig == nil {
-		return nil, nil
-	}
-	path := absolutePath(*c.productVariables.DexpreoptGlobalConfig)
-	ctx.AddNinjaFileDeps(path)
-	return ioutil.ReadFile(path)
+func (c *config) DexpreoptGlobalConfig() string {
+	return String(c.productVariables.DexpreoptGlobalConfig)
 }
 
 func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
diff --git a/android/env.go b/android/env.go
index 46bd3d6..d9f2db2 100644
--- a/android/env.go
+++ b/android/env.go
@@ -52,17 +52,6 @@
 	os.Clearenv()
 }
 
-// getenv checks either os.Getenv or originalEnv so that it works before or after the init()
-// function above.  It doesn't add any dependencies on the environment variable, so it should
-// only be used for values that won't change.  For values that might change use ctx.Config().Getenv.
-func getenv(key string) string {
-	if originalEnv == nil {
-		return os.Getenv(key)
-	} else {
-		return originalEnv[key]
-	}
-}
-
 func EnvSingleton() Singleton {
 	return &envSingleton{}
 }
@@ -77,12 +66,7 @@
 		return
 	}
 
-	data, err := env.EnvFileContents(envDeps)
-	if err != nil {
-		ctx.Errorf(err.Error())
-	}
-
-	err = WriteFileToOutputDir(envFile, data, 0666)
+	err := env.WriteEnvFile(envFile.String(), envDeps)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
diff --git a/android/makevars.go b/android/makevars.go
index aba4cce..38a028c 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -23,6 +23,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -40,6 +41,7 @@
 	Config() Config
 	DeviceConfig() DeviceConfig
 	AddNinjaFileDeps(deps ...string)
+	Fs() pathtools.FileSystem
 
 	ModuleName(module blueprint.Module) string
 	ModuleDir(module blueprint.Module) string
@@ -149,8 +151,7 @@
 		return
 	}
 
-	outFile := absolutePath(PathForOutput(ctx,
-		"make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
+	outFile := PathForOutput(ctx, "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()
 
 	if ctx.Failed() {
 		return
@@ -174,15 +175,15 @@
 
 	outBytes := s.writeVars(vars)
 
-	if _, err := os.Stat(absolutePath(outFile)); err == nil {
-		if data, err := ioutil.ReadFile(absolutePath(outFile)); err == nil {
+	if _, err := os.Stat(outFile); err == nil {
+		if data, err := ioutil.ReadFile(outFile); err == nil {
 			if bytes.Equal(data, outBytes) {
 				return
 			}
 		}
 	}
 
-	if err := ioutil.WriteFile(absolutePath(outFile), outBytes, 0666); err != nil {
+	if err := ioutil.WriteFile(outFile, outBytes, 0666); err != nil {
 		ctx.Errorf(err.Error())
 	}
 }
diff --git a/android/module.go b/android/module.go
index 67d1f12..c998007 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,13 +16,13 @@
 
 import (
 	"fmt"
-	"os"
 	"path"
 	"path/filepath"
 	"strings"
 	"text/scanner"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -91,8 +91,7 @@
 
 	Glob(globPattern string, excludes []string) Paths
 	GlobFiles(globPattern string, excludes []string) Paths
-	IsSymlink(path Path) bool
-	Readlink(path Path) string
+	Fs() pathtools.FileSystem
 }
 
 // BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
@@ -1173,22 +1172,6 @@
 	return pathsForModuleSrcFromFullPath(e, ret, false)
 }
 
-func (b *earlyModuleContext) IsSymlink(path Path) bool {
-	fileInfo, err := b.config.fs.Lstat(path.String())
-	if err != nil {
-		b.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err)
-	}
-	return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink
-}
-
-func (b *earlyModuleContext) Readlink(path Path) string {
-	dest, err := b.config.fs.Readlink(path.String())
-	if err != nil {
-		b.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err)
-	}
-	return dest
-}
-
 func (e *earlyModuleContext) Module() Module {
 	module, _ := e.EarlyModuleContext.Module().(Module)
 	return module
diff --git a/android/package_ctx.go b/android/package_ctx.go
index a228910..d3527fa 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 )
 
 // PackageContext is a wrapper for blueprint.PackageContext that adds
@@ -59,6 +60,10 @@
 	e.pctx.AddNinjaFileDeps(deps...)
 }
 
+func (e *configErrorWrapper) Fs() pathtools.FileSystem {
+	return nil
+}
+
 type PackageVarContext interface {
 	PathContext
 	errorfContext
diff --git a/android/paths.go b/android/paths.go
index 02f56d0..a03fe17 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -16,8 +16,6 @@
 
 import (
 	"fmt"
-	"io/ioutil"
-	"os"
 	"path/filepath"
 	"reflect"
 	"sort"
@@ -27,11 +25,10 @@
 	"github.com/google/blueprint/pathtools"
 )
 
-var absSrcDir string
-
 // PathContext is the subset of a (Module|Singleton)Context required by the
 // Path methods.
 type PathContext interface {
+	Fs() pathtools.FileSystem
 	Config() Config
 	AddNinjaFileDeps(deps ...string)
 }
@@ -393,7 +390,7 @@
 		return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
 	} else {
 		p := pathForModuleSrc(ctx, s)
-		if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
+		if exists, _, err := ctx.Fs().Exists(p.String()); err != nil {
 			reportPathErrorf(ctx, "%s: %s", p, err.Error())
 		} else if !exists {
 			reportPathErrorf(ctx, "module source path %q does not exist", p)
@@ -723,7 +720,7 @@
 		var deps []string
 		// We cannot add build statements in this context, so we fall back to
 		// AddNinjaFileDeps
-		files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
+		files, deps, err = pathtools.Glob(path.String(), nil, pathtools.FollowSymlinks)
 		ctx.AddNinjaFileDeps(deps...)
 	}
 
@@ -755,7 +752,7 @@
 		if !exists {
 			modCtx.AddMissingDependencies([]string{path.String()})
 		}
-	} else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
+	} else if exists, _, err := ctx.Fs().Exists(path.String()); err != nil {
 		reportPathErrorf(ctx, "%s: %s", path, err.Error())
 	} else if !exists {
 		reportPathErrorf(ctx, "source path %q does not exist", path)
@@ -1359,6 +1356,7 @@
 	config Config
 }
 
+func (x *testPathContext) Fs() pathtools.FileSystem   { return x.config.fs }
 func (x *testPathContext) Config() Config             { return x.config }
 func (x *testPathContext) AddNinjaFileDeps(...string) {}
 
@@ -1404,16 +1402,3 @@
 	}
 	return rel, true, nil
 }
-
-// Writes a file to the output directory.  Attempting to write directly to the output directory
-// will fail due to the sandbox of the soong_build process.
-func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
-	return ioutil.WriteFile(absolutePath(path.String()), data, perm)
-}
-
-func absolutePath(path string) string {
-	if filepath.IsAbs(path) {
-		return path
-	}
-	return filepath.Join(absSrcDir, path)
-}
diff --git a/android/paths_test.go b/android/paths_test.go
index 46e3e1f..ec5e598 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -21,6 +21,7 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -206,6 +207,10 @@
 	inRoot         bool
 }
 
+func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
+	return pathtools.MockFs(nil)
+}
+
 func (m moduleInstallPathContextImpl) Config() Config {
 	return m.baseModuleContext.config
 }
diff --git a/android/register.go b/android/register.go
index b48d3d1..b5defec 100644
--- a/android/register.go
+++ b/android/register.go
@@ -84,9 +84,7 @@
 }
 
 func NewContext() *Context {
-	ctx := &Context{blueprint.NewContext()}
-	ctx.SetSrcDir(absSrcDir)
-	return ctx
+	return &Context{blueprint.NewContext()}
 }
 
 func (ctx *Context) Register() {
diff --git a/android/sandbox.go b/android/sandbox.go
deleted file mode 100644
index ed022fb..0000000
--- a/android/sandbox.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2020 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import (
-	"fmt"
-	"os"
-)
-
-func init() {
-	// Stash the working directory in a private variable and then change the working directory
-	// to "/", which will prevent untracked accesses to files by Go Soong plugins. The
-	// SOONG_SANDBOX_SOONG_BUILD environment variable is set by soong_ui, and is not
-	// overrideable on the command line.
-
-	orig, err := os.Getwd()
-	if err != nil {
-		panic(fmt.Errorf("failed to get working directory: %s", err))
-	}
-	absSrcDir = orig
-
-	if getenv("SOONG_SANDBOX_SOONG_BUILD") == "true" {
-		err = os.Chdir("/")
-		if err != nil {
-			panic(fmt.Errorf("failed to change working directory to '/': %s", err))
-		}
-	}
-}
-
-// DO NOT USE THIS FUNCTION IN NEW CODE.
-// Deprecated: This function will be removed as soon as the existing use cases that use it have been
-// replaced.
-func AbsSrcDirForExistingUseCases() string {
-	return absSrcDir
-}
diff --git a/android/singleton.go b/android/singleton.go
index 91268ad..5519ca0 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -16,6 +16,7 @@
 
 import (
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 )
 
 // SingletonContext
@@ -73,6 +74,8 @@
 	// builder whenever a file matching the pattern as added or removed, without rerunning if a
 	// file that does not match the pattern is added to a searched directory.
 	GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+	Fs() pathtools.FileSystem
 }
 
 type singletonAdaptor struct {
diff --git a/androidmk/Android.bp b/androidmk/Android.bp
index 70fc1f7..4110073 100644
--- a/androidmk/Android.bp
+++ b/androidmk/Android.bp
@@ -41,6 +41,7 @@
         "androidmk-parser",
         "blueprint-parser",
         "bpfix-lib",
+        "soong-android",
     ],
 }
 
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 8860984..0082d8b 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -15,9 +15,9 @@
 package androidmk
 
 import (
+	"android/soong/android"
 	mkparser "android/soong/androidmk/parser"
 	"fmt"
-	"sort"
 	"strings"
 
 	bpparser "github.com/google/blueprint/parser"
@@ -350,13 +350,7 @@
 		return err
 	}
 
-	var classifications []string
-	for classification := range namesByClassification {
-		classifications = append(classifications, classification)
-	}
-	sort.Strings(classifications)
-
-	for _, nameClassification := range classifications {
+	for _, nameClassification := range android.SortedStringKeys(namesByClassification) {
 		name := namesByClassification[nameClassification]
 		if component, ok := lists[nameClassification]; ok && !emptyList(component) {
 			err = setVariable(ctx.file, ctx.append, ctx.prefix, name, component, true)
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index f7d9081..97d21f4 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -76,7 +76,7 @@
 	// Link all handmade CMakeLists.txt aggregate from
 	//     BASE/development/ide/clion to
 	// BASE/out/development/ide/clion.
-	dir := filepath.Join(android.AbsSrcDirForExistingUseCases(), cLionAggregateProjectsDirectory)
+	dir := filepath.Join(getAndroidSrcRootDirectory(ctx), cLionAggregateProjectsDirectory)
 	filepath.Walk(dir, linkAggregateCMakeListsFiles)
 
 	return
@@ -147,7 +147,7 @@
 	f.WriteString("# Tools > CMake > Change Project Root  \n\n")
 	f.WriteString(fmt.Sprintf("cmake_minimum_required(VERSION %s)\n", minimumCMakeVersionSupported))
 	f.WriteString(fmt.Sprintf("project(%s)\n", ccModule.ModuleBase.Name()))
-	f.WriteString(fmt.Sprintf("set(ANDROID_ROOT %s)\n\n", android.AbsSrcDirForExistingUseCases()))
+	f.WriteString(fmt.Sprintf("set(ANDROID_ROOT %s)\n\n", getAndroidSrcRootDirectory(ctx)))
 
 	pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
 	f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang"))
@@ -465,7 +465,7 @@
 }
 
 func getCMakeListsForModule(module *Module, ctx android.SingletonContext) string {
-	return filepath.Join(android.AbsSrcDirForExistingUseCases(),
+	return filepath.Join(getAndroidSrcRootDirectory(ctx),
 		cLionOutputProjectsDirectory,
 		path.Dir(ctx.BlueprintFile(module)),
 		module.ModuleBase.Name()+"-"+
@@ -473,3 +473,8 @@
 			module.ModuleBase.Os().Name,
 		cMakeListsFilename)
 }
+
+func getAndroidSrcRootDirectory(ctx android.SingletonContext) string {
+	srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
+	return srcPath
+}
diff --git a/cc/compdb.go b/cc/compdb.go
index ea12443..dff14db 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -79,9 +79,9 @@
 
 	// Create the output file.
 	dir := android.PathForOutput(ctx, compdbOutputProjectsDirectory)
-	os.MkdirAll(filepath.Join(android.AbsSrcDirForExistingUseCases(), dir.String()), 0777)
+	os.MkdirAll(dir.String(), 0777)
 	compDBFile := dir.Join(ctx, compdbFilename)
-	f, err := os.Create(filepath.Join(android.AbsSrcDirForExistingUseCases(), compDBFile.String()))
+	f, err := os.Create(compDBFile.String())
 	if err != nil {
 		log.Fatalf("Could not create file %s: %s", compDBFile, err)
 	}
@@ -103,8 +103,8 @@
 	}
 	f.Write(dat)
 
-	if finalLinkDir := ctx.Config().Getenv(envVariableCompdbLink); finalLinkDir != "" {
-		finalLinkPath := filepath.Join(finalLinkDir, compdbFilename)
+	finalLinkPath := filepath.Join(ctx.Config().Getenv(envVariableCompdbLink), compdbFilename)
+	if finalLinkPath != "" {
 		os.Remove(finalLinkPath)
 		if err := os.Symlink(compDBFile.String(), finalLinkPath); err != nil {
 			log.Fatalf("Unable to symlink %s to %s: %s", compDBFile, finalLinkPath, err)
@@ -174,17 +174,18 @@
 		return
 	}
 
-	pathToCC, err := ctx.Eval(pctx, "${config.ClangBin}")
+	rootDir := getCompdbAndroidSrcRootDirectory(ctx)
+	pathToCC, err := ctx.Eval(pctx, rootDir+"/${config.ClangBin}/")
 	ccPath := "/bin/false"
 	cxxPath := "/bin/false"
 	if err == nil {
-		ccPath = filepath.Join(pathToCC, "clang")
-		cxxPath = filepath.Join(pathToCC, "clang++")
+		ccPath = pathToCC + "clang"
+		cxxPath = pathToCC + "clang++"
 	}
 	for _, src := range srcs {
 		if _, ok := builds[src.String()]; !ok {
 			builds[src.String()] = compDbEntry{
-				Directory: android.AbsSrcDirForExistingUseCases(),
+				Directory: rootDir,
 				Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath),
 				File:      src.String(),
 			}
@@ -199,3 +200,8 @@
 	}
 	return []string{""}, err
 }
+
+func getCompdbAndroidSrcRootDirectory(ctx android.SingletonContext) string {
+	srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
+	return srcPath
+}
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 5744bb2..b8423be 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"os"
 	"path/filepath"
 	"strings"
 
@@ -254,8 +255,16 @@
 	depsPath := android.PathForSource(ctx, "bionic/libc/versioner-dependencies")
 	depsGlob := ctx.Glob(filepath.Join(depsPath.String(), "**/*"), nil)
 	for i, path := range depsGlob {
-		if ctx.IsSymlink(path) {
-			dest := ctx.Readlink(path)
+		fileInfo, err := os.Lstat(path.String())
+		if err != nil {
+			ctx.ModuleErrorf("os.Lstat(%q) failed: %s", path.String, err)
+		}
+		if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
+			dest, err := os.Readlink(path.String())
+			if err != nil {
+				ctx.ModuleErrorf("os.Readlink(%q) failed: %s",
+					path.String, err)
+			}
 			// Additional .. to account for the symlink itself.
 			depsGlob[i] = android.PathForSource(
 				ctx, filepath.Clean(filepath.Join(path.String(), "..", dest)))
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 2a929c5..0c79ccc 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -16,6 +16,7 @@
 
 import (
 	"encoding/json"
+	"io/ioutil"
 	"strings"
 
 	"android/soong/android"
@@ -184,7 +185,7 @@
 // soongConfig argument. LoadGlobalConfig is used directly in Soong and in
 // dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by
 // Make.
-func LoadGlobalConfig(ctx android.PathContext, data []byte, soongConfig GlobalSoongConfig) (GlobalConfig, error) {
+func LoadGlobalConfig(ctx android.PathContext, path string, soongConfig GlobalSoongConfig) (GlobalConfig, []byte, error) {
 	type GlobalJSONConfig struct {
 		GlobalConfig
 
@@ -195,9 +196,9 @@
 	}
 
 	config := GlobalJSONConfig{}
-	err := json.Unmarshal(data, &config)
+	data, err := loadConfig(ctx, path, &config)
 	if err != nil {
-		return config.GlobalConfig, err
+		return config.GlobalConfig, nil, err
 	}
 
 	// Construct paths that require a PathContext.
@@ -208,13 +209,13 @@
 	// either CreateGlobalSoongConfig or LoadGlobalSoongConfig).
 	config.GlobalConfig.SoongConfig = soongConfig
 
-	return config.GlobalConfig, nil
+	return config.GlobalConfig, data, nil
 }
 
 // LoadModuleConfig reads a per-module dexpreopt.config file into a ModuleConfig struct.  It is not used in Soong, which
 // receives a ModuleConfig struct directly from java/dexpreopt.go.  It is used in dexpreopt_gen called from oMake to
 // read the module dexpreopt.config written by Make.
-func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error) {
+func LoadModuleConfig(ctx android.PathContext, path string) (ModuleConfig, error) {
 	type ModuleJSONConfig struct {
 		ModuleConfig
 
@@ -232,7 +233,7 @@
 
 	config := ModuleJSONConfig{}
 
-	err := json.Unmarshal(data, &config)
+	_, err := loadConfig(ctx, path, &config)
 	if err != nil {
 		return config.ModuleConfig, err
 	}
@@ -288,10 +289,10 @@
 
 // LoadGlobalSoongConfig reads the dexpreopt_soong.config file into a
 // GlobalSoongConfig struct. It is only used in dexpreopt_gen.
-func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongConfig, error) {
+func LoadGlobalSoongConfig(ctx android.PathContext, path string) (GlobalSoongConfig, error) {
 	var jc globalJsonSoongConfig
 
-	err := json.Unmarshal(data, &jc)
+	_, err := loadConfig(ctx, path, &jc)
 	if err != nil {
 		return GlobalSoongConfig{}, err
 	}
@@ -351,6 +352,26 @@
 	}, " "))
 }
 
+func loadConfig(ctx android.PathContext, path string, config interface{}) ([]byte, error) {
+	r, err := ctx.Fs().Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer r.Close()
+
+	data, err := ioutil.ReadAll(r)
+	if err != nil {
+		return nil, err
+	}
+
+	err = json.Unmarshal(data, config)
+	if err != nil {
+		return nil, err
+	}
+
+	return data, nil
+}
+
 func GlobalConfigForTests(ctx android.PathContext) GlobalConfig {
 	return GlobalConfig{
 		DisablePreopt:                      false,
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index e2818bb..d2faa00 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -18,7 +18,6 @@
 	"bytes"
 	"flag"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -42,6 +41,7 @@
 	config android.Config
 }
 
+func (x *pathContext) Fs() pathtools.FileSystem   { return pathtools.OsFs }
 func (x *pathContext) Config() android.Config     { return x.config }
 func (x *pathContext) AddNinjaFileDeps(...string) {}
 
@@ -76,39 +76,21 @@
 		usage("--module configuration file is required")
 	}
 
-	ctx := &pathContext{android.NullConfig(*outDir)}
+	ctx := &pathContext{android.TestConfig(*outDir, nil, "", nil)}
 
-	globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalSoongConfigPath, err)
-		os.Exit(2)
-	}
-
-	globalSoongConfig, err := dexpreopt.LoadGlobalSoongConfig(ctx, globalSoongConfigData)
+	globalSoongConfig, err := dexpreopt.LoadGlobalSoongConfig(ctx, *globalSoongConfigPath)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalSoongConfigPath, err)
 		os.Exit(2)
 	}
 
-	globalConfigData, err := ioutil.ReadFile(*globalConfigPath)
+	globalConfig, _, err := dexpreopt.LoadGlobalConfig(ctx, *globalConfigPath, globalSoongConfig)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalConfigPath, err)
+		fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalConfigPath, err)
 		os.Exit(2)
 	}
 
-	globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, globalConfigData, globalSoongConfig)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "error parse global config %q: %s\n", *globalConfigPath, err)
-		os.Exit(2)
-	}
-
-	moduleConfigData, err := ioutil.ReadFile(*moduleConfigPath)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "error reading module config %q: %s\n", *moduleConfigPath, err)
-		os.Exit(2)
-	}
-
-	moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, moduleConfigData)
+	moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, *moduleConfigPath)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error loading module config %q: %s\n", *moduleConfigPath, err)
 		os.Exit(2)
diff --git a/env/env.go b/env/env.go
index a98e1f6..bf58a99 100644
--- a/env/env.go
+++ b/env/env.go
@@ -27,7 +27,7 @@
 type envFileEntry struct{ Key, Value string }
 type envFileData []envFileEntry
 
-func EnvFileContents(envDeps map[string]string) ([]byte, error) {
+func WriteEnvFile(filename string, envDeps map[string]string) error {
 	contents := make(envFileData, 0, len(envDeps))
 	for key, value := range envDeps {
 		contents = append(contents, envFileEntry{key, value})
@@ -37,12 +37,17 @@
 
 	data, err := json.MarshalIndent(contents, "", "    ")
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	data = append(data, '\n')
 
-	return data, nil
+	err = ioutil.WriteFile(filename, data, 0664)
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 func StaleEnvFile(filename string) (bool, error) {
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index f3191e7..35748b8 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -36,11 +36,10 @@
 
 func dexpreoptGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
 	return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
-		if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
-			panic(err)
-		} else if data != nil {
+		if f := ctx.Config().DexpreoptGlobalConfig(); f != "" {
 			soongConfig := dexpreopt.CreateGlobalSoongConfig(ctx)
-			globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, data, soongConfig)
+			ctx.AddNinjaFileDeps(f)
+			globalConfig, data, err := dexpreopt.LoadGlobalConfig(ctx, f, soongConfig)
 			if err != nil {
 				panic(err)
 			}
diff --git a/java/jdeps.go b/java/jdeps.go
index 49e3de3..fccc40f 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -17,6 +17,7 @@
 import (
 	"encoding/json"
 	"fmt"
+	"os"
 
 	"android/soong/android"
 )
@@ -91,21 +92,23 @@
 		moduleInfos[name] = dpInfo
 	})
 
-	jfpath := android.PathForOutput(ctx, jdepsJsonFileName)
+	jfpath := android.PathForOutput(ctx, jdepsJsonFileName).String()
 	err := createJsonFile(moduleInfos, jfpath)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
 }
 
-func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath android.WritablePath) error {
+func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath string) error {
+	file, err := os.Create(jfpath)
+	if err != nil {
+		return fmt.Errorf("Failed to create file: %s, relative: %v", jdepsJsonFileName, err)
+	}
+	defer file.Close()
 	buf, err := json.MarshalIndent(moduleInfos, "", "\t")
 	if err != nil {
-		return fmt.Errorf("JSON marshal of java deps failed: %s", err)
+		return fmt.Errorf("Write file failed: %s, relative: %v", jdepsJsonFileName, err)
 	}
-	err = android.WriteFileToOutputDir(jfpath, buf, 0666)
-	if err != nil {
-		return fmt.Errorf("Writing java deps to %s failed: %s", jfpath.String(), err)
-	}
+	fmt.Fprintf(file, string(buf))
 	return nil
 }
diff --git a/ui/build/soong.go b/ui/build/soong.go
index afbc073..3388417 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -119,7 +119,6 @@
 			"-j", strconv.Itoa(config.Parallel()),
 			"--frontend_file", fifo,
 			"-f", filepath.Join(config.SoongOutDir(), file))
-		cmd.Environment.Set("SOONG_SANDBOX_SOONG_BUILD", "true")
 		cmd.Sandbox = soongSandbox
 		cmd.RunAndStreamOrFatal()
 	}