Merge changes I6d1be1a9,Ie5e9310e

* changes:
  Skip mainline_sdk by default in build_test.bash
  Allow multiple --skip-products and --products arguments to multiproduct_kati
diff --git a/android/androidmk.go b/android/androidmk.go
index b32048a..062dcb3 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -820,7 +820,7 @@
 	}
 
 	return !module.Enabled() ||
-		module.commonProperties.SkipInstall ||
+		module.commonProperties.HideFromMake ||
 		// Make does not understand LinuxBionic
 		module.Os() == LinuxBionic
 }
diff --git a/android/module.go b/android/module.go
index 429f311..cfb32c1 100644
--- a/android/module.go
+++ b/android/module.go
@@ -452,8 +452,8 @@
 	InstallInRoot() bool
 	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
-	SkipInstall()
-	IsSkipInstall() bool
+	HideFromMake()
+	IsHideFromMake() bool
 	MakeUninstallable()
 	ReplacedByPrebuilt()
 	IsReplacedByPrebuilt() bool
@@ -751,6 +751,13 @@
 	// Set by osMutator.
 	CommonOSVariant bool `blueprint:"mutated"`
 
+	// When HideFromMake is set to true, no entry for this variant will be emitted in the
+	// generated Android.mk file.
+	HideFromMake bool `blueprint:"mutated"`
+
+	// When SkipInstall is set to true, calls to ctx.InstallFile, ctx.InstallExecutable,
+	// ctx.InstallSymlink and ctx.InstallAbsoluteSymlink act like calls to ctx.PackageFile
+	// and don't create a rule to install the file.
 	SkipInstall bool `blueprint:"mutated"`
 
 	// Whether the module has been replaced by a prebuilt
@@ -1355,26 +1362,33 @@
 	m.commonProperties.ForcedDisabled = true
 }
 
+// HideFromMake marks this variant so that it is not emitted in the generated Android.mk file.
+func (m *ModuleBase) HideFromMake() {
+	m.commonProperties.HideFromMake = true
+}
+
+// IsHideFromMake returns true if HideFromMake was previously called.
+func (m *ModuleBase) IsHideFromMake() bool {
+	return m.commonProperties.HideFromMake == true
+}
+
+// SkipInstall marks this variant to not create install rules when ctx.Install* are called.
 func (m *ModuleBase) SkipInstall() {
 	m.commonProperties.SkipInstall = true
 }
 
-func (m *ModuleBase) IsSkipInstall() bool {
-	return m.commonProperties.SkipInstall == true
-}
-
-// Similar to SkipInstall, but if the AndroidMk entry would set
+// Similar to HideFromMake, but if the AndroidMk entry would set
 // LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
 // rather than leaving it out altogether. That happens in cases where it would
 // have other side effects, in particular when it adds a NOTICE file target,
 // which other install targets might depend on.
 func (m *ModuleBase) MakeUninstallable() {
-	m.SkipInstall()
+	m.HideFromMake()
 }
 
 func (m *ModuleBase) ReplacedByPrebuilt() {
 	m.commonProperties.ReplacedByPrebuilt = true
-	m.SkipInstall()
+	m.HideFromMake()
 }
 
 func (m *ModuleBase) IsReplacedByPrebuilt() bool {
@@ -2440,11 +2454,15 @@
 	return m.module.InstallForceOS()
 }
 
-func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool {
+func (m *moduleContext) skipInstall() bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
 	}
 
+	if m.module.base().commonProperties.HideFromMake {
+		return true
+	}
+
 	// We'll need a solution for choosing which of modules with the same name in different
 	// namespaces to install.  For now, reuse the list of namespaces exported to Make as the
 	// list of namespaces to install in a Soong-only build.
@@ -2492,7 +2510,7 @@
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
 
-	if !m.skipInstall(fullInstallPath) {
+	if !m.skipInstall() {
 		deps = append(deps, m.module.base().installFilesDepSet.ToList().Paths()...)
 
 		var implicitDeps, orderOnlyDeps Paths
@@ -2526,6 +2544,7 @@
 	m.packageFile(fullInstallPath, srcPath, executable)
 
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
+
 	return fullInstallPath
 }
 
@@ -2537,7 +2556,7 @@
 	if err != nil {
 		panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
 	}
-	if !m.skipInstall(fullInstallPath) {
+	if !m.skipInstall() {
 
 		m.Build(pctx, BuildParams{
 			Rule:        Symlink,
@@ -2570,7 +2589,7 @@
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
 
-	if !m.skipInstall(fullInstallPath) {
+	if !m.skipInstall() {
 		m.Build(pctx, BuildParams{
 			Rule:        Symlink,
 			Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
@@ -2738,6 +2757,7 @@
 // Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to
 // specify that they can be used as a tool by a genrule module.
 type HostToolProvider interface {
+	Module
 	// HostToolPath returns the path to the host tool for the module if it is one, or an invalid
 	// OptionalPath.
 	HostToolPath() OptionalPath
diff --git a/android/override_module.go b/android/override_module.go
index f8342d5..fa08566 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -235,7 +235,7 @@
 			return
 		}
 		// See if there's a prebuilt module that overrides this override module with prefer flag,
-		// in which case we call SkipInstall on the corresponding variant later.
+		// in which case we call HideFromMake on the corresponding variant later.
 		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
 			prebuilt, ok := dep.(PrebuiltInterface)
 			if !ok {
@@ -284,7 +284,7 @@
 			mods[i+1].(OverridableModule).override(ctx, o)
 			if o.getOverriddenByPrebuilt() {
 				// The overriding module itself, too, is overridden by a prebuilt. Skip its installation.
-				mods[i+1].SkipInstall()
+				mods[i+1].HideFromMake()
 			}
 		}
 	} else if o, ok := ctx.Module().(OverrideModule); ok {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 8114a65..39d30c5 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -289,7 +289,7 @@
 				})
 			}
 		} else {
-			m.SkipInstall()
+			m.HideFromMake()
 		}
 	}
 }
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index b00dc2f..516d042 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -86,6 +86,9 @@
 
 func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
 	if makeVar := String(t.properties.Export_to_make_var); makeVar != "" {
+		if t.Target().Os != BuildOs {
+			return
+		}
 		ctx.StrictRaw(makeVar, t.toolPath.String())
 	}
 }
diff --git a/android/rule_builder.go b/android/rule_builder.go
index e2d8187..84501fe 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -32,6 +32,7 @@
 
 const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
 const sboxOutSubDir = "out"
+const sboxToolsSubDir = "tools"
 const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
 
 // RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
@@ -48,6 +49,7 @@
 	highmem          bool
 	remoteable       RemoteRuleSupports
 	outDir           WritablePath
+	sboxTools        bool
 	sboxManifestPath WritablePath
 	missingDeps      []string
 }
@@ -140,6 +142,19 @@
 	return r
 }
 
+// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
+// sandbox.
+func (r *RuleBuilder) SandboxTools() *RuleBuilder {
+	if !r.sbox {
+		panic("SandboxTools() must be called after Sbox()")
+	}
+	if len(r.commands) > 0 {
+		panic("SandboxTools() may not be called after Command()")
+	}
+	r.sboxTools = true
+	return r
+}
+
 // Install associates an output of the rule with an install location, which can be retrieved later using
 // RuleBuilder.Installs.
 func (r *RuleBuilder) Install(from Path, to string) {
@@ -468,8 +483,29 @@
 			manifest.OutputDepfile = proto.String(depFile.String())
 		}
 
+		// If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
+		// into the sbox directory.
+		if r.sboxTools {
+			for _, tool := range tools {
+				command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+					From: proto.String(tool.String()),
+					To:   proto.String(sboxPathForToolRel(r.ctx, tool)),
+				})
+			}
+			for _, c := range r.commands {
+				for _, tool := range c.packagedTools {
+					command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+						From:       proto.String(tool.srcPath.String()),
+						To:         proto.String(sboxPathForPackagedToolRel(tool)),
+						Executable: proto.Bool(tool.executable),
+					})
+					tools = append(tools, tool.srcPath)
+				}
+			}
+		}
+
 		// Add copy rules to the manifest to copy each output file from the sbox directory.
-		// to the output directory.
+		// to the output directory after running the commands.
 		sboxOutputs := make([]string, len(outputs))
 		for i, output := range outputs {
 			rel := Rel(r.ctx, r.outDir.String(), output.String())
@@ -582,6 +618,7 @@
 	symlinkOutputs WritablePaths
 	depFiles       WritablePaths
 	tools          Paths
+	packagedTools  []PackagingSpec
 	rspFileInputs  Paths
 
 	// spans [start,end) of the command that should not be ninja escaped
@@ -625,6 +662,79 @@
 	return path.String()
 }
 
+// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
+// the corresponding path for the tool in the sbox sandbox.  It assumes that sandboxing and tool
+// sandboxing are enabled.
+func SboxPathForTool(ctx BuilderContext, path Path) string {
+	return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
+}
+
+func sboxPathForToolRel(ctx BuilderContext, path Path) string {
+	// Errors will be handled in RuleBuilder.Build where we have a context to report them
+	relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
+	if isRelOut {
+		// The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
+		return filepath.Join(sboxToolsSubDir, "out", relOut)
+	}
+	// The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
+	return filepath.Join(sboxToolsSubDir, "src", path.String())
+}
+
+// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
+// tool after copying it into the sandbox.  This can be used  on the RuleBuilder command line to
+// reference the tool.
+func SboxPathForPackagedTool(spec PackagingSpec) string {
+	return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+}
+
+func sboxPathForPackagedToolRel(spec PackagingSpec) string {
+	return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
+}
+
+// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
+// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
+// if it is not.  This can be used  on the RuleBuilder command line to reference the tool.
+func (c *RuleBuilderCommand) PathForTool(path Path) string {
+	if c.rule.sbox && c.rule.sboxTools {
+		return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
+	}
+	return path.String()
+}
+
+// PackagedTool adds the specified tool path to the command line.  It can only be used with tool
+// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
+func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
+	if !c.rule.sboxTools {
+		panic("PackagedTool() requires SandboxTools()")
+	}
+
+	c.packagedTools = append(c.packagedTools, spec)
+	c.Text(sboxPathForPackagedToolRel(spec))
+	return c
+}
+
+// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
+// line.  It can only be used with tool sandboxing enabled by SandboxTools().
+func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
+	if !c.rule.sboxTools {
+		panic("ImplicitPackagedTool() requires SandboxTools()")
+	}
+
+	c.packagedTools = append(c.packagedTools, spec)
+	return c
+}
+
+// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
+// line.  It can only be used with tool sandboxing enabled by SandboxTools().
+func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
+	if !c.rule.sboxTools {
+		panic("ImplicitPackagedTools() requires SandboxTools()")
+	}
+
+	c.packagedTools = append(c.packagedTools, specs...)
+	return c
+}
+
 // Text adds the specified raw text to the command line.  The text should not contain input or output paths or the
 // rule will not have them listed in its dependencies or outputs.
 func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
@@ -693,7 +803,19 @@
 // RuleBuilder.Tools.
 func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
 	c.tools = append(c.tools, path)
-	return c.Text(path.String())
+	return c.Text(c.PathForTool(path))
+}
+
+// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
+func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
+	c.tools = append(c.tools, path)
+	return c
+}
+
+// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
+func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
+	c.tools = append(c.tools, paths...)
+	return c
 }
 
 // BuiltTool adds the specified tool path that was built using a host Soong module to the command line.  The path will
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index e676e4a..06ea124 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -436,6 +436,44 @@
 			t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n                   got %#v", w, g)
 		}
 	})
+
+	t.Run("sbox tools", func(t *testing.T) {
+		rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
+			PathForOutput(ctx, "sbox.textproto")).SandboxTools()
+		addCommands(rule)
+
+		wantCommands := []string{
+			"__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output Input __SBOX_SANDBOX_DIR__/out/Output __SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
+			"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
+			"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
+		}
+
+		wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
+
+		if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
+			t.Errorf("\nwant rule.Commands() = %#v\n                   got %#v", w, g)
+		}
+
+		if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.Inputs() = %#v\n                 got %#v", w, g)
+		}
+		if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.Outputs() = %#v\n                  got %#v", w, g)
+		}
+		if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.DepFiles() = %#v\n                  got %#v", w, g)
+		}
+		if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.Tools() = %#v\n                got %#v", w, g)
+		}
+		if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.OrderOnlys() = %#v\n                got %#v", w, g)
+		}
+
+		if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+			t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n                   got %#v", w, g)
+		}
+	})
 }
 
 func testRuleBuilderFactory() Module {
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 142a813..5a6917e 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -158,11 +158,7 @@
 			return []error{fmt.Errorf("bool_variable name must not be blank")}
 		}
 
-		mt.Variables = append(mt.Variables, &boolVariable{
-			baseVariable: baseVariable{
-				variable: name,
-			},
-		})
+		mt.Variables = append(mt.Variables, newBoolVariable(name))
 	}
 
 	for _, name := range props.Value_variables {
@@ -420,6 +416,9 @@
 
 // PropertiesToApply returns the applicable properties from a ModuleType that should be applied
 // based on SoongConfig values.
+// Expects that props contains a struct field with name soong_config_variables. The fields within
+// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
+// props should be generated via CreateProperties.
 func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
 	var ret []interface{}
 	props = props.Elem().FieldByName(soongConfigProperty)
@@ -505,6 +504,14 @@
 	baseVariable
 }
 
+func newBoolVariable(name string) *boolVariable {
+	return &boolVariable{
+		baseVariable{
+			variable: name,
+		},
+	}
+}
+
 func (b boolVariable) variableValuesType() reflect.Type {
 	return emptyInterfaceType
 }
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index 4190016..fb0e189 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -17,6 +17,8 @@
 import (
 	"reflect"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func Test_CanonicalizeToProperty(t *testing.T) {
@@ -247,3 +249,72 @@
 		})
 	}
 }
+
+type properties struct {
+	A *string
+	B bool
+}
+type soongConfigVariables struct {
+	Bool_var       properties
+	Other_bool_var properties
+}
+
+type soongConfigProps struct {
+	Soong_config_variables soongConfigVariables
+}
+
+func Test_PropertiesToApply(t *testing.T) {
+
+	mt := &ModuleType{
+		BaseModuleType:  "foo",
+		ConfigNamespace: "bar",
+		Variables: []soongConfigVariable{
+			newBoolVariable("bool_var"),
+			newBoolVariable("other_bool_var"),
+		},
+		affectableProperties: []string{
+			"a",
+			"b",
+		},
+	}
+	props := soongConfigProps{
+		Soong_config_variables: soongConfigVariables{
+			Bool_var: properties{
+				A: proptools.StringPtr("a"),
+				B: true,
+			},
+			Other_bool_var: properties{
+				A: proptools.StringPtr("other"),
+				B: false,
+			},
+		},
+	}
+
+	testCases := []struct {
+		config    SoongConfig
+		wantProps []interface{}
+	}{
+		{
+			config: Config(map[string]string{}),
+		},
+		{
+			config:    Config(map[string]string{"bool_var": "y"}),
+			wantProps: []interface{}{props.Soong_config_variables.Bool_var},
+		},
+		{
+			config:    Config(map[string]string{"other_bool_var": "y"}),
+			wantProps: []interface{}{props.Soong_config_variables.Other_bool_var},
+		},
+	}
+
+	for _, tc := range testCases {
+		gotProps, err := PropertiesToApply(mt, reflect.ValueOf(&props), tc.config)
+		if err != nil {
+			t.Errorf("Unexpected error in PropertiesToApply: %s", err)
+		}
+
+		if !reflect.DeepEqual(gotProps, tc.wantProps) {
+			t.Errorf("Expected %s, got %s", tc.wantProps, gotProps)
+		}
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 93dac3d..753ddd7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -126,13 +126,14 @@
 		}
 
 		Arc struct {
-			Cflags       []string
-			Exclude_srcs []string
-			Include_dirs []string
-			Shared_libs  []string
-			Static_libs  []string
-			Srcs         []string
-		}
+			Cflags            []string `android:"arch_variant"`
+			Exclude_srcs      []string `android:"arch_variant"`
+			Include_dirs      []string `android:"arch_variant"`
+			Shared_libs       []string `android:"arch_variant"`
+			Static_libs       []string `android:"arch_variant"`
+			Srcs              []string `android:"arch_variant"`
+			Whole_static_libs []string `android:"arch_variant"`
+		} `android:"arch_variant"`
 
 		Flatten_apex struct {
 			Enabled *bool
diff --git a/apex/apex.go b/apex/apex.go
index 88d93af..9e66254 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1183,6 +1183,9 @@
 // Implements cc.Coverage
 func (a *apexBundle) HideFromMake() {
 	a.properties.HideFromMake = true
+	// This HideFromMake is shadowing the ModuleBase one, call through to it for now.
+	// TODO(ccross): untangle these
+	a.ModuleBase.HideFromMake()
 }
 
 // Implements cc.Coverage
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 69b1dbb..4a6aecf 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -6578,7 +6578,7 @@
 								continue
 							}
 							mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module)
-							if !mod.Enabled() || mod.IsSkipInstall() {
+							if !mod.Enabled() || mod.IsHideFromMake() {
 								continue
 							}
 							for _, ent := range android.AndroidMkEntriesForTest(t, config, "", mod) {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index ce16d73..7931e9e 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -218,7 +218,7 @@
 	})
 
 	if p.prebuiltCommon.checkForceDisable(ctx) {
-		p.SkipInstall()
+		p.HideFromMake()
 		return
 	}
 
@@ -392,7 +392,7 @@
 		})
 
 	if a.prebuiltCommon.checkForceDisable(ctx) {
-		a.SkipInstall()
+		a.HideFromMake()
 		return
 	}
 
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 187a2ff..ddacb70 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -273,7 +273,7 @@
 			entries.SubName = "." + library.stubsVersion()
 		}
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-			// library.makeUninstallable() depends on this to bypass SkipInstall() for
+			// library.makeUninstallable() depends on this to bypass HideFromMake() for
 			// static libraries.
 			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
 			if library.buildStubs() {
diff --git a/cc/cc.go b/cc/cc.go
index 240080f..1cc2bd5 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -432,6 +432,7 @@
 	mustUseVendorVariant() bool
 	nativeCoverage() bool
 	directlyInAnyApex() bool
+	isPreventInstall() bool
 }
 
 type ModuleContext interface {
@@ -1325,6 +1326,10 @@
 	return ctx.mod.DirectlyInAnyApex()
 }
 
+func (ctx *moduleContextImpl) isPreventInstall() bool {
+	return ctx.mod.Properties.PreventInstall
+}
+
 func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	return &Module{
 		hod:      hod,
@@ -1575,18 +1580,24 @@
 		}
 	}
 
-	if c.installable(apexInfo) {
+	if !proptools.BoolDefault(c.Properties.Installable, true) {
+		// If the module has been specifically configure to not be installed then
+		// hide from make as otherwise it will break when running inside make
+		// as the output path to install will not be specified. Not all uninstallable
+		// modules can be hidden from make as some are needed for resolving make side
+		// dependencies.
+		c.HideFromMake()
+	} else if !c.installable(apexInfo) {
+		c.SkipInstall()
+	}
+
+	// Still call c.installer.install though, the installs will be stored as PackageSpecs
+	// to allow using the outputs in a genrule.
+	if c.installer != nil && c.outputFile.Valid() {
 		c.installer.install(ctx, c.outputFile.Path())
 		if ctx.Failed() {
 			return
 		}
-	} else if !proptools.BoolDefault(c.Properties.Installable, true) {
-		// If the module has been specifically configure to not be installed then
-		// skip the installation as otherwise it will break when running inside make
-		// as the output path to install will not be specified. Not all uninstallable
-		// modules can skip installation as some are needed for resolving make side
-		// dependencies.
-		c.SkipInstall()
 	}
 }
 
diff --git a/cc/image.go b/cc/image.go
index 32325b9..cca454a 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -454,7 +454,7 @@
 		vndkVersion := ctx.DeviceConfig().VndkVersion()
 		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
 			m.Properties.HideFromMake = true
-			m.SkipInstall()
+			m.HideFromMake()
 		}
 	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
 		m.Properties.ImageVariationPrefix = ProductVariationPrefix
diff --git a/cc/library.go b/cc/library.go
index 959d670..11ee7dd 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1336,7 +1336,7 @@
 			}
 		} else if ctx.directlyInAnyApex() && ctx.isLlndk(ctx.Config()) && !isBionic(ctx.baseModuleName()) {
 			// Skip installing LLNDK (non-bionic) libraries moved to APEX.
-			ctx.Module().SkipInstall()
+			ctx.Module().HideFromMake()
 		}
 
 		library.baseInstaller.install(ctx, file)
@@ -1345,7 +1345,7 @@
 	if Bool(library.Properties.Static_ndk_lib) && library.static() &&
 		!ctx.useVndk() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() &&
 		library.baseLinker.sanitize.isUnsanitizedVariant() &&
-		!library.buildStubs() && ctx.sdkVersion() == "" {
+		ctx.isForPlatform() && !ctx.isPreventInstall() {
 		installPath := getNdkSysrootBase(ctx).Join(
 			ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
 
diff --git a/cc/sabi.go b/cc/sabi.go
index 99e718e..c357f8d 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -131,7 +131,7 @@
 	// Module is shared library type.
 
 	// Don't check uninstallable modules.
-	if m.IsSkipInstall() {
+	if m.IsHideFromMake() {
 		return false
 	}
 
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index b291bd0..f5f8121 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -836,7 +836,7 @@
 	// Disables source modules if corresponding snapshot exists.
 	if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() {
 		// But do not disable because the shared variant depends on the static variant.
-		module.SkipInstall()
+		module.HideFromMake()
 		module.Properties.HideFromMake = true
 	} else {
 		module.Disable()
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index da37d0f..bca76dc 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -181,8 +181,8 @@
 		return false
 	}
 	// When android/prebuilt.go selects between source and prebuilt, it sets
-	// SkipInstall on the other one to avoid duplicate install rules in make.
-	if m.IsSkipInstall() {
+	// HideFromMake on the other one to avoid duplicate install rules in make.
+	if m.IsHideFromMake() {
 		return false
 	}
 	// skip proprietary modules, but (for the vendor snapshot only)
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index e6e2ad8..04162cd 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -130,7 +130,7 @@
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
 	if !p.matchesWithDevice(ctx.DeviceConfig()) {
-		ctx.Module().SkipInstall()
+		ctx.Module().HideFromMake()
 		return nil
 	}
 
@@ -175,7 +175,7 @@
 		return in
 	}
 
-	ctx.Module().SkipInstall()
+	ctx.Module().HideFromMake()
 	return nil
 }
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 594d253..8df32f2 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -139,7 +139,6 @@
 	// number of shards the input files are sharded into.
 	taskGenerator taskFunc
 
-	deps        android.Paths
 	rule        blueprint.Rule
 	rawCommands []string
 
@@ -244,6 +243,8 @@
 		}
 	}
 
+	var tools android.Paths
+	var packagedTools []android.PackagingSpec
 	if len(g.properties.Tools) > 0 {
 		seenTools := make(map[string]bool)
 
@@ -251,37 +252,52 @@
 			switch tag := ctx.OtherModuleDependencyTag(module).(type) {
 			case hostToolDependencyTag:
 				tool := ctx.OtherModuleName(module)
-				var path android.OptionalPath
 
-				if t, ok := module.(android.HostToolProvider); ok {
+				switch t := module.(type) {
+				case android.HostToolProvider:
+					// A HostToolProvider provides the path to a tool, which will be copied
+					// into the sandbox.
 					if !t.(android.Module).Enabled() {
 						if ctx.Config().AllowMissingDependencies() {
 							ctx.AddMissingDependencies([]string{tool})
 						} else {
 							ctx.ModuleErrorf("depends on disabled module %q", tool)
 						}
-						break
+						return
 					}
-					path = t.HostToolPath()
-				} else if t, ok := module.(bootstrap.GoBinaryTool); ok {
+					path := t.HostToolPath()
+					if !path.Valid() {
+						ctx.ModuleErrorf("host tool %q missing output file", tool)
+						return
+					}
+					if specs := t.TransitivePackagingSpecs(); specs != nil {
+						// If the HostToolProvider has PackgingSpecs, which are definitions of the
+						// required relative locations of the tool and its dependencies, use those
+						// instead.  They will be copied to those relative locations in the sbox
+						// sandbox.
+						packagedTools = append(packagedTools, specs...)
+						// Assume that the first PackagingSpec of the module is the tool.
+						addLocationLabel(tag.label, []string{android.SboxPathForPackagedTool(specs[0])})
+					} else {
+						tools = append(tools, path.Path())
+						addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, path.Path())})
+					}
+				case bootstrap.GoBinaryTool:
+					// A GoBinaryTool provides the install path to a tool, which will be copied.
 					if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil {
-						path = android.OptionalPathForPath(android.PathForOutput(ctx, s))
+						toolPath := android.PathForOutput(ctx, s)
+						tools = append(tools, toolPath)
+						addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, toolPath)})
 					} else {
 						ctx.ModuleErrorf("cannot find path for %q: %v", tool, err)
-						break
+						return
 					}
-				} else {
+				default:
 					ctx.ModuleErrorf("%q is not a host tool provider", tool)
-					break
+					return
 				}
 
-				if path.Valid() {
-					g.deps = append(g.deps, path.Path())
-					addLocationLabel(tag.label, []string{path.Path().String()})
-					seenTools[tag.label] = true
-				} else {
-					ctx.ModuleErrorf("host tool %q missing output file", tool)
-				}
+				seenTools[tag.label] = true
 			}
 		})
 
@@ -305,8 +321,12 @@
 
 	for _, toolFile := range g.properties.Tool_files {
 		paths := android.PathsForModuleSrc(ctx, []string{toolFile})
-		g.deps = append(g.deps, paths...)
-		addLocationLabel(toolFile, paths.Strings())
+		tools = append(tools, paths...)
+		var sandboxPaths []string
+		for _, path := range paths {
+			sandboxPaths = append(sandboxPaths, android.SboxPathForTool(ctx, path))
+		}
+		addLocationLabel(toolFile, sandboxPaths)
 	}
 
 	var srcFiles android.Paths
@@ -358,7 +378,7 @@
 		manifestPath := android.PathForModuleOut(ctx, manifestName)
 
 		// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
-		rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath)
+		rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath).SandboxTools()
 		cmd := rule.Command()
 
 		for _, out := range task.out {
@@ -448,8 +468,9 @@
 		cmd.Text(rawCommand)
 		cmd.ImplicitOutputs(task.out)
 		cmd.Implicits(task.in)
-		cmd.Implicits(g.deps)
-		cmd.Implicits(task.extraTools)
+		cmd.ImplicitTools(tools)
+		cmd.ImplicitTools(task.extraTools)
+		cmd.ImplicitPackagedTools(packagedTools)
 		if Bool(g.properties.Depfile) {
 			cmd.ImplicitDepFile(task.depFile)
 		}
@@ -619,7 +640,7 @@
 			// TODO(ccross): this RuleBuilder is a hack to be able to call
 			// rule.Command().PathForOutput.  Replace this with passing the rule into the
 			// generator.
-			rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil)
+			rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil).SandboxTools()
 
 			for _, in := range shard {
 				outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
@@ -672,7 +693,8 @@
 				outputDepfile = android.PathForModuleGen(ctx, genSubDir, "gensrcs.d")
 				depFixerTool := ctx.Config().HostToolPath(ctx, "dep_fixer")
 				fullCommand += fmt.Sprintf(" && %s -o $(depfile) %s",
-					depFixerTool.String(), strings.Join(commandDepFiles, " "))
+					android.SboxPathForTool(ctx, depFixerTool),
+					strings.Join(commandDepFiles, " "))
 				extraTools = append(extraTools, depFixerTool)
 			}
 
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 6ae9a18..2f5605e 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -141,7 +141,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "empty location tool2",
@@ -150,7 +150,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "empty location tool file",
@@ -159,7 +159,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "empty location tool file fg",
@@ -168,7 +168,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "empty location tool and tool file",
@@ -178,7 +178,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool",
@@ -187,7 +187,7 @@
 				out: ["out"],
 				cmd: "$(location tool) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool2",
@@ -196,7 +196,7 @@
 				out: ["out"],
 				cmd: "$(location :tool) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool file",
@@ -205,7 +205,7 @@
 				out: ["out"],
 				cmd: "$(location tool_file1) > $(out)",
 			`,
-			expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool file fg",
@@ -214,7 +214,7 @@
 				out: ["out"],
 				cmd: "$(location :1tool_file) > $(out)",
 			`,
-			expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool files",
@@ -223,7 +223,7 @@
 				out: ["out"],
 				cmd: "$(locations :tool_files) > $(out)",
 			`,
-			expect: "tool_file1 tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 __SBOX_SANDBOX_DIR__/tools/src/tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "in1",
@@ -600,7 +600,7 @@
 				cmd: "$(location) $(in) > $(out)",
 			`,
 			cmds: []string{
-				"bash -c 'out/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c 'out/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
+				"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
 			},
 			deps:  []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
 			files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
@@ -614,8 +614,8 @@
 				shard_size: 2,
 			`,
 			cmds: []string{
-				"bash -c 'out/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c 'out/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
-				"bash -c 'out/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
+				"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
+				"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
 			},
 			deps:  []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
 			files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
@@ -758,7 +758,7 @@
 }
 
 func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	t.outputFile = android.PathForTesting("out", ctx.ModuleName())
+	t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
 }
 
 func (t *testTool) HostToolPath() android.OptionalPath {
diff --git a/rust/image.go b/rust/image.go
index 4951d2b..af8c3b2 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -90,7 +90,7 @@
 		vndkVersion := ctx.DeviceConfig().VndkVersion()
 		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
 			m.Properties.HideFromMake = true
-			m.SkipInstall()
+			m.HideFromMake()
 		}
 	}
 }
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index 1be3b8a..4b08ac3 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -82,6 +82,9 @@
 # CrossHost: linux_bionic
 # CrossHostArch: x86_64
 #   -  Enable Bionic on host as ART needs prebuilts for it.
+# VendorVars.art_mdoule.source_build
+#   -  TODO(b/172480615): Change default to false when platform uses ART Module
+#      prebuilts by default.
 cat > ${SOONG_VARS}.new << EOF
 {
     "BuildNumberFile": "build_number.txt",
@@ -104,7 +107,7 @@
 
     "VendorVars": {
         "art_module": {
-            "source_build": "${ENABLE_ART_SOURCE_BUILD:-false}"
+            "source_build": "${ENABLE_ART_SOURCE_BUILD:-true}"
         }
     },
 
diff --git a/ui/metrics/proc/Android.bp b/ui/metrics/proc/Android.bp
new file mode 100644
index 0000000..32d8217
--- /dev/null
+++ b/ui/metrics/proc/Android.bp
@@ -0,0 +1,37 @@
+// 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.
+
+bootstrap_go_package {
+    name: "soong-ui-metrics-proc",
+    pkgPath: "android/soong/ui/metrics/proc",
+    deps: [
+        "soong-finder-fs",
+    ],
+    srcs: [
+        "status.go",
+    ],
+    linux: {
+        srcs: [
+            "status_linux.go",
+        ],
+        testSrcs: [
+            "status_linux_test.go",
+        ],
+    },
+    darwin: {
+        srcs: [
+            "status_darwin.go",
+        ],
+    },
+}
diff --git a/ui/metrics/proc/status.go b/ui/metrics/proc/status.go
new file mode 100644
index 0000000..f8b734c
--- /dev/null
+++ b/ui/metrics/proc/status.go
@@ -0,0 +1,128 @@
+// package proc contains functionality to read proc status files.
+package proc
+
+import (
+	"strconv"
+	"strings"
+)
+
+// ProcStatus holds information regarding the memory usage of
+// an executing process. The memory sizes in each of the field
+// is in bytes.
+type ProcStatus struct {
+	// Process PID.
+	pid int
+
+	// Peak virtual memory size.
+	VmPeak uint64
+
+	// Virtual memory size.
+	VmSize uint64
+
+	// Locked Memory size.
+	VmLck uint64
+
+	// Pinned memory size.
+	VmPin uint64
+
+	// Peak resident set size.
+	VmHWM uint64
+
+	// Resident set size (sum of RssAnon, RssFile and RssShmem).
+	VmRss uint64
+
+	// Size of resident anonymous memory.
+	RssAnon uint64
+
+	// Size of resident shared memory.
+	RssShmem uint64
+
+	// Size of data segments.
+	VmData uint64
+
+	// Size of stack segments.
+	VmStk uint64
+
+	//Size of text segments.
+	VmExe uint64
+
+	//Shared library code size.
+	VmLib uint64
+
+	// Page table entries size.
+	VmPTE uint64
+
+	// Size of second-level page tables.
+	VmPMD uint64
+
+	// Swapped-out virtual memory size by anonymous private.
+	VmSwap uint64
+
+	// Size of hugetlb memory page size.
+	HugetlbPages uint64
+}
+
+// fillProcStatus takes the key and value, converts the value
+// to the proper size unit and is stored in the ProcStatus.
+func fillProcStatus(s *ProcStatus, key, value string) {
+	v := strToUint64(value)
+	switch key {
+	case "VmPeak":
+		s.VmPeak = v
+	case "VmSize":
+		s.VmSize = v
+	case "VmLck":
+		s.VmLck = v
+	case "VmPin":
+		s.VmPin = v
+	case "VmHWM":
+		s.VmHWM = v
+	case "VmRSS":
+		s.VmRss = v
+	case "RssAnon":
+		s.RssAnon = v
+	case "RssShmem":
+		s.RssShmem = v
+	case "VmData":
+		s.VmData = v
+	case "VmStk":
+		s.VmStk = v
+	case "VmExe":
+		s.VmExe = v
+	case "VmLib":
+		s.VmLib = v
+	case "VmPTE":
+		s.VmPTE = v
+	case "VmPMD":
+		s.VmPMD = v
+	case "VmSwap":
+		s.VmSwap = v
+	case "HugetlbPages":
+		s.HugetlbPages = v
+	}
+}
+
+// strToUint64 takes the string and converts to unsigned 64-bit integer.
+// If the string contains a memory unit such as kB and is converted to
+// bytes.
+func strToUint64(v string) uint64 {
+	// v could be "1024 kB" so scan for the empty space and
+	// split between the value and the unit.
+	var separatorIndex int
+	if separatorIndex = strings.IndexAny(v, " "); separatorIndex < 0 {
+		separatorIndex = len(v)
+	}
+	value, err := strconv.ParseUint(v[:separatorIndex], 10, 64)
+	if err != nil {
+		return 0
+	}
+
+	var scale uint64 = 1
+	switch strings.TrimSpace(v[separatorIndex:]) {
+	case "kB", "KB":
+		scale = 1024
+	case "mB", "MB":
+		scale = 1024 * 1024
+	}
+	return value * scale
+}
diff --git a/ui/metrics/proc/status_darwin.go b/ui/metrics/proc/status_darwin.go
new file mode 100644
index 0000000..5c788a5
--- /dev/null
+++ b/ui/metrics/proc/status_darwin.go
@@ -0,0 +1,11 @@
+package proc
+
+import (
+	"android/soong/finder/fs"
+)
+
+// NewProcStatus returns a zero filled value of ProcStatus as it
+// is not supported for darwin distribution based.
+func NewProcStatus(pid int, _ fs.FileSystem) (*ProcStatus, error) {
+	return &ProcStatus{}, nil
+}
diff --git a/ui/metrics/proc/status_linux.go b/ui/metrics/proc/status_linux.go
new file mode 100644
index 0000000..dc0f943
--- /dev/null
+++ b/ui/metrics/proc/status_linux.go
@@ -0,0 +1,46 @@
+package proc
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"android/soong/finder/fs"
+)
+
+// NewProcStatus returns an instance of the ProcStatus that contains memory
+// information of the process. The memory information is extracted from the
+// "/proc/<pid>/status" text file. This is only available for Linux
+// distribution that supports /proc.
+func NewProcStatus(pid int, fileSystem fs.FileSystem) (*ProcStatus, error) {
+	statusFname := filepath.Join("/proc", strconv.Itoa(pid), "status")
+	r, err := fileSystem.Open(statusFname)
+	if err != nil {
+		return &ProcStatus{}, err
+	}
+	defer r.Close()
+
+	data, err := ioutil.ReadAll(r)
+	if err != nil {
+		return &ProcStatus{}, err
+	}
+
+	s := &ProcStatus{
+		pid: pid,
+	}
+
+	for _, l := range strings.Split(string(data), "\n") {
+		// If the status file does not contain "key: values", just skip the line
+		// as the information we are looking for is not needed.
+		if !strings.Contains(l, ":") {
+			continue
+		}
+
+		// At this point, we're only considering entries that has key, single value pairs.
+		kv := strings.SplitN(l, ":", 2)
+		fillProcStatus(s, strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]))
+	}
+
+	return s, nil
+}
diff --git a/ui/metrics/proc/status_linux_test.go b/ui/metrics/proc/status_linux_test.go
new file mode 100644
index 0000000..6709850
--- /dev/null
+++ b/ui/metrics/proc/status_linux_test.go
@@ -0,0 +1,112 @@
+package proc
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"strconv"
+	"testing"
+
+	"android/soong/finder/fs"
+)
+
+func TestNewProcStatus(t *testing.T) {
+	fs := fs.NewMockFs(nil)
+
+	pid := 4032827
+	procDir := filepath.Join("/proc", strconv.Itoa(pid))
+	if err := fs.MkDirs(procDir); err != nil {
+		t.Fatalf("failed to create proc pid dir %s: %v", procDir, err)
+	}
+	statusFilename := filepath.Join(procDir, "status")
+
+	if err := fs.WriteFile(statusFilename, statusData, 0644); err != nil {
+		t.Fatalf("failed to write proc file %s: %v", statusFilename, err)
+	}
+
+	status, err := NewProcStatus(pid, fs)
+	if err != nil {
+		t.Fatalf("got %v, want nil for error", err)
+	}
+
+	fmt.Printf("%d %d\b", status.VmPeak, expectedStatus.VmPeak)
+	if !reflect.DeepEqual(status, expectedStatus) {
+		t.Errorf("got %v, expecting %v for ProcStatus", status, expectedStatus)
+	}
+}
+
+var statusData = []byte(`Name:   fake_process
+Umask:  0022
+State:  S (sleeping)
+Tgid:   4032827
+Ngid:   0
+Pid:    4032827
+PPid:   1
+TracerPid:      0
+Uid:    0       0       0       0
+Gid:    0       0       0       0
+FDSize: 512
+Groups:
+NStgid: 4032827
+NSpid:  4032827
+NSpgid: 4032827
+NSsid:  4032827
+VmPeak:   733232 kB
+VmSize:   733232 kB
+VmLck:       132 kB
+VmPin:       130 kB
+VmHWM:     69156 kB
+VmRSS:     69156 kB
+RssAnon:           50896 kB
+RssFile:           18260 kB
+RssShmem:            122 kB
+VmData:   112388 kB
+VmStk:       132 kB
+VmExe:      9304 kB
+VmLib:         8 kB
+VmPTE:       228 kB
+VmSwap:        10 kB
+HugetlbPages:          22 kB
+CoreDumping:    0
+THP_enabled:    1
+Threads:        46
+SigQ:   2/767780
+SigPnd: 0000000000000000
+ShdPnd: 0000000000000000
+SigBlk: fffffffe3bfa3a00
+SigIgn: 0000000000000000
+SigCgt: fffffffe7fc1feff
+CapInh: 0000000000000000
+CapPrm: 0000003fffffffff
+CapEff: 0000003fffffffff
+CapBnd: 0000003fffffffff
+CapAmb: 0000000000000000
+NoNewPrivs:     0
+Seccomp:        0
+Speculation_Store_Bypass:       thread vulnerable
+Cpus_allowed:   ff,ffffffff,ffffffff
+Cpus_allowed_list:      0-71
+Mems_allowed:   00000000,00000003
+Mems_allowed_list:      0-1
+voluntary_ctxt_switches:        1635
+nonvoluntary_ctxt_switches:     32
+`)
+
+var expectedStatus = &ProcStatus{
+	pid:          4032827,
+	VmPeak:       750829568,
+	VmSize:       750829568,
+	VmLck:        135168,
+	VmPin:        133120,
+	VmHWM:        70815744,
+	VmRss:        70815744,
+	RssAnon:      52117504,
+	RssShmem:     124928,
+	VmData:       115085312,
+	VmStk:        135168,
+	VmExe:        9527296,
+	VmLib:        8192,
+	VmPTE:        233472,
+	VmSwap:       10240,
+	HugetlbPages: 22528,
+}