Merge "Docs: Add link to related Soong docs on SAC"
diff --git a/Android.bp b/Android.bp
index b4a9d30..97f786e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -243,6 +243,7 @@
         "java/droiddoc.go",
         "java/gen.go",
         "java/genrule.go",
+        "java/hiddenapi.go",
         "java/jacoco.go",
         "java/java.go",
         "java/jdeps.go",
diff --git a/OWNERS b/OWNERS
index 892beb7..7983c19 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,4 @@
-per-file * = ccross@android.com,dwillemsen@google.com,jungjw@google.com
+per-file * = asmundak@google.com,ccross@android.com,dwillemsen@google.com,jungjw@google.com
 per-file ndk_*.go, *gen_stub_libs.py = danalbert@google.com
 per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
 per-file tidy.go = srhines@google.com, chh@google.com
diff --git a/android/config.go b/android/config.go
index 09d9cbc..cf4d6e5 100644
--- a/android/config.go
+++ b/android/config.go
@@ -983,6 +983,18 @@
 	return c.productVariables.EnforceSystemCertificateWhitelist
 }
 
+func (c *config) HiddenAPIPublicList() string {
+	return String(c.productVariables.HiddenAPIPublicList)
+}
+
+func (c *config) HiddenAPIFlags() string {
+	return String(c.productVariables.HiddenAPIFlags)
+}
+
+func (c *config) HiddenAPIExtraAppUsageJars() []string {
+	return c.productVariables.HiddenAPIExtraAppUsageJars
+}
+
 func stringSlice(s *[]string) []string {
 	if s != nil {
 		return *s
diff --git a/android/module.go b/android/module.go
index aed16b3..551824d 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -1268,6 +1269,10 @@
 
 	if !a.skipInstall(fullInstallPath) {
 
+		relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String())
+		if err != nil {
+			panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
+		}
 		a.Build(pctx, BuildParams{
 			Rule:        Symlink,
 			Description: "install symlink " + fullInstallPath.Base(),
@@ -1275,7 +1280,7 @@
 			OrderOnly:   Paths{srcPath},
 			Default:     !a.Config().EmbeddedInMake(),
 			Args: map[string]string{
-				"fromPath": srcPath.String(),
+				"fromPath": relPath,
 			},
 		})
 
diff --git a/android/variable.go b/android/variable.go
index e19d858..46b1155 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -271,6 +271,11 @@
 
 	EnforceSystemCertificate          *bool    `json:",omitempty"`
 	EnforceSystemCertificateWhitelist []string `json:",omitempty"`
+
+	// TODO(ccross): move these to a Singleton in Soong
+	HiddenAPIPublicList        *string  `json:",omitempty"`
+	HiddenAPIFlags             *string  `json:",omitempty"`
+	HiddenAPIExtraAppUsageJars []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/java/androidmk.go b/java/androidmk.go
index 7b81273..b9e4298 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -28,6 +28,11 @@
 		fmt.Fprintln(w, "LOCAL_MODULE := "+name+"-hostdex")
 		fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
 		fmt.Fprintln(w, "LOCAL_MODULE_CLASS := JAVA_LIBRARIES")
+		if library.dexJarFile != nil {
+			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.dexJarFile.String())
+		} else {
+			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", library.implementationAndResourcesJar.String())
+		}
 		if library.installFile == nil {
 			fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
 		}
diff --git a/java/app.go b/java/app.go
index 4bae78a..e44737e 100644
--- a/java/app.go
+++ b/java/app.go
@@ -261,7 +261,7 @@
 
 	certificates = append([]Certificate{a.certificate}, certificateDeps...)
 
-	packageFile := android.PathForModuleOut(ctx, "package.apk")
+	packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".apk")
 	CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates)
 
 	if !a.Module.Platform() {
diff --git a/java/config/config.go b/java/config/config.go
index 92cfb84..5c838a5 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -154,4 +154,7 @@
 	pctx.SourcePathsVariable("ManifestMergerClasspath", ":", ManifestMergerClasspath...)
 
 	pctx.HostBinToolVariable("ZipAlign", "zipalign")
+
+	pctx.HostBinToolVariable("Class2Greylist", "class2greylist")
+	pctx.HostBinToolVariable("HiddenAPI", "hiddenapi")
 }
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 01adaa7..156ee26 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -78,4 +78,7 @@
 	ctx.Strict("ANDROID_MANIFEST_MERGER_DEPS", "${ManifestMergerJars}")
 	ctx.Strict("ANDROID_MANIFEST_MERGER",
 		"${JavaCmd} -classpath ${ManifestMergerClasspath} com.android.manifmerger.Merger")
+
+	ctx.Strict("CLASS2GREYLIST", "${Class2Greylist}")
+	ctx.Strict("HIDDENAPI", "${HiddenAPI}")
 }
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
new file mode 100644
index 0000000..de72e7c
--- /dev/null
+++ b/java/hiddenapi.go
@@ -0,0 +1,145 @@
+// Copyright 2019 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 java
+
+import (
+	"sort"
+	"strings"
+	"sync"
+
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", blueprint.RuleParams{
+	Command:     "${config.Class2Greylist} --public-api-list ${publicAPIList} $in $outFlag $out",
+	CommandDeps: []string{"${config.Class2Greylist}"},
+}, "outFlag", "publicAPIList")
+
+func hiddenAPIGenerateCSV(ctx android.ModuleContext, classesJar android.Path) {
+	flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
+	metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv")
+	publicList := &bootImagePath{ctx.Config().HiddenAPIPublicList()}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        hiddenAPIGenerateCSVRule,
+		Description: "hiddenapi flags",
+		Input:       classesJar,
+		Output:      flagsCSV,
+		Implicit:    publicList,
+		Args: map[string]string{
+			"outFlag":       "--write-flags-csv",
+			"publicAPIList": publicList.String(),
+		},
+	})
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        hiddenAPIGenerateCSVRule,
+		Description: "hiddenapi metadata",
+		Input:       classesJar,
+		Output:      metadataCSV,
+		Implicit:    publicList,
+		Args: map[string]string{
+			"outFlag":       "--write-metadata-csv",
+			"publicAPIList": publicList.String(),
+		},
+	})
+
+	hiddenAPISaveCSVOutputs(ctx, flagsCSV, metadataCSV)
+}
+
+var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
+	Command: `rm -rf $tmpDir && mkdir -p $tmpDir && mkdir $tmpDir/dex-input && mkdir $tmpDir/dex-output && ` +
+		`unzip -o -q $in 'classes*.dex' -d $tmpDir/dex-input && ` +
+		`for INPUT_DEX in $$(find $tmpDir/dex-input -maxdepth 1 -name 'classes*.dex' | sort); do ` +
+		`  echo "--input-dex=$${INPUT_DEX}"; ` +
+		`  echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})"; ` +
+		`done | xargs ${config.HiddenAPI} encode --api-flags=$flags && ` +
+		`${config.SoongZipCmd} -o $tmpDir/dex.jar -C $tmpDir/dex-output -f "$tmpDir/dex-output/classes*.dex" && ` +
+		`${config.MergeZipsCmd} -D -zipToNotStrip $tmpDir/dex.jar -stripFile "classes*.dex" $out $tmpDir/dex.jar $in`,
+	CommandDeps: []string{
+		"${config.HiddenAPI}",
+		"${config.SoongZipCmd}",
+		"${config.MergeZipsCmd}",
+	},
+}, "flags", "tmpDir")
+
+func hiddenAPIEncodeDex(ctx android.ModuleContext, output android.WritablePath, dexInput android.WritablePath) {
+	flags := &bootImagePath{ctx.Config().HiddenAPIFlags()}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        hiddenAPIEncodeDexRule,
+		Description: "hiddenapi encode dex",
+		Input:       dexInput,
+		Output:      output,
+		Implicit:    flags,
+		Args: map[string]string{
+			"flags":  flags.String(),
+			"tmpDir": android.PathForModuleOut(ctx, "hiddenapi", "dex").String(),
+		},
+	})
+
+	hiddenAPISaveDexInputs(ctx, dexInput)
+}
+
+const hiddenAPIOutputsKey = "hiddenAPIOutputsKey"
+
+var hiddenAPIOutputsLock sync.Mutex
+
+func hiddenAPIGetOutputs(config android.Config) (*android.Paths, *android.Paths, *android.Paths) {
+	type threePathsPtrs [3]*android.Paths
+	s := config.Once(hiddenAPIOutputsKey, func() interface{} {
+		return threePathsPtrs{new(android.Paths), new(android.Paths), new(android.Paths)}
+	}).(threePathsPtrs)
+	return s[0], s[1], s[2]
+}
+
+func hiddenAPISaveCSVOutputs(ctx android.ModuleContext, flagsCSV, metadataCSV android.Path) {
+	flagsCSVList, metadataCSVList, _ := hiddenAPIGetOutputs(ctx.Config())
+
+	hiddenAPIOutputsLock.Lock()
+	defer hiddenAPIOutputsLock.Unlock()
+
+	*flagsCSVList = append(*flagsCSVList, flagsCSV)
+	*metadataCSVList = append(*metadataCSVList, metadataCSV)
+}
+
+func hiddenAPISaveDexInputs(ctx android.ModuleContext, dexInput android.Path) {
+	_, _, dexInputList := hiddenAPIGetOutputs(ctx.Config())
+
+	hiddenAPIOutputsLock.Lock()
+	defer hiddenAPIOutputsLock.Unlock()
+
+	*dexInputList = append(*dexInputList, dexInput)
+}
+
+func init() {
+	android.RegisterMakeVarsProvider(pctx, hiddenAPIMakeVars)
+}
+
+func hiddenAPIMakeVars(ctx android.MakeVarsContext) {
+	flagsCSVList, metadataCSVList, dexInputList := hiddenAPIGetOutputs(ctx.Config())
+
+	export := func(name string, paths *android.Paths) {
+		s := paths.Strings()
+		sort.Strings(s)
+		ctx.Strict(name, strings.Join(s, " "))
+	}
+
+	export("SOONG_HIDDENAPI_FLAGS", flagsCSVList)
+	export("SOONG_HIDDENAPI_GREYLIST_METADATA", metadataCSVList)
+	export("SOONG_HIDDENAPI_DEX_INPUTS", dexInputList)
+}
diff --git a/java/java.go b/java/java.go
index 2ac5a5b..209d0a7 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1172,12 +1172,27 @@
 	j.implementationAndResourcesJar = implementationAndResourcesJar
 
 	if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.deviceProperties.Compile_dex)) {
+		// Dex compilation
 		var dexOutputFile android.ModuleOutPath
 		dexOutputFile = j.compileDex(ctx, flags, outputFile, jarName)
 		if ctx.Failed() {
 			return
 		}
 
+		// Hidden API CSV generation and dex encoding
+		if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+			isBootJar := inList(ctx.ModuleName(), ctx.Config().BootJars())
+			if isBootJar || inList(ctx.ModuleName(), ctx.Config().HiddenAPIExtraAppUsageJars()) {
+				// Derive the greylist from classes jar.
+				hiddenAPIGenerateCSV(ctx, j.implementationJarFile)
+			}
+			if isBootJar {
+				hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", jarName)
+				hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexOutputFile)
+				dexOutputFile = hiddenAPIJar
+			}
+		}
+
 		// merge dex jar with resources if necessary
 		if j.resourceJar != nil {
 			jars := android.Paths{dexOutputFile, j.resourceJar}
@@ -1189,6 +1204,7 @@
 
 		j.dexJarFile = dexOutputFile
 
+		// Dexpreopting
 		j.dexpreopter.isInstallable = Bool(j.properties.Installable)
 		j.dexpreopter.uncompressedDex = j.deviceProperties.UncompressDex
 		dexOutputFile = j.dexpreopt(ctx, dexOutputFile)
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 1d8684b..1c0ee34 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -96,7 +96,6 @@
 	"javap":     Allowed,
 	"lsof":      Allowed,
 	"m4":        Allowed,
-	"md5sum":    Allowed,
 	"openssl":   Allowed,
 	"patch":     Allowed,
 	"pgrep":     Allowed,
@@ -110,14 +109,10 @@
 	"rsync":     Allowed,
 	"sed":       Allowed,
 	"sh":        Allowed,
-	"sha1sum":   Allowed,
-	"sha256sum": Allowed,
-	"sha512sum": Allowed,
 	"tar":       Allowed,
 	"timeout":   Allowed,
 	"tr":        Allowed,
 	"unzip":     Allowed,
-	"wc":        Allowed,
 	"which":     Allowed,
 	"xz":        Allowed,
 	"zip":       Allowed,
@@ -137,46 +132,51 @@
 	"ld.gold":    Forbidden,
 	"pkg-config": Forbidden,
 
-	// On linux we'll use the toybox version of these instead
-	"basename": Toybox,
-	"cat":      Toybox,
-	"chmod":    Toybox,
-	"cmp":      Toybox,
-	"comm":     Toybox,
-	"cut":      Toybox,
-	"dirname":  Toybox,
-	"du":       Toybox,
-	"echo":     Toybox,
-	"env":      Toybox,
-	"expr":     Toybox,
-	"head":     Toybox,
-	"getconf":  Toybox,
-	"id":       Toybox,
-	"ln":       Toybox,
-	"ls":       Toybox,
-	"mkdir":    Toybox,
-	"mktemp":   Toybox,
-	"mv":       Toybox,
-	"od":       Toybox,
-	"paste":    Toybox,
-	"pwd":      Toybox,
-	"readlink": Toybox,
-	"rm":       Toybox,
-	"rmdir":    Toybox,
-	"setsid":   Toybox,
-	"sleep":    Toybox,
-	"sort":     Toybox,
-	"stat":     Toybox,
-	"tail":     Toybox,
-	"tee":      Toybox,
-	"touch":    Toybox,
-	"true":     Toybox,
-	"uname":    Toybox,
-	"uniq":     Toybox,
-	"unix2dos": Toybox,
-	"whoami":   Toybox,
-	"xargs":    Toybox,
-	"xxd":      Toybox,
+	// On Linux we'll use the toybox versions of these instead.
+	"basename":  Toybox,
+	"cat":       Toybox,
+	"chmod":     Toybox,
+	"cmp":       Toybox,
+	"comm":      Toybox,
+	"cut":       Toybox,
+	"dirname":   Toybox,
+	"du":        Toybox,
+	"echo":      Toybox,
+	"env":       Toybox,
+	"expr":      Toybox,
+	"head":      Toybox,
+	"getconf":   Toybox,
+	"id":        Toybox,
+	"ln":        Toybox,
+	"ls":        Toybox,
+	"md5sum":    Toybox,
+	"mkdir":     Toybox,
+	"mktemp":    Toybox,
+	"mv":        Toybox,
+	"od":        Toybox,
+	"paste":     Toybox,
+	"pwd":       Toybox,
+	"readlink":  Toybox,
+	"rm":        Toybox,
+	"rmdir":     Toybox,
+	"setsid":    Toybox,
+	"sha1sum":   Toybox,
+	"sha256sum": Toybox,
+	"sha512sum": Toybox,
+	"sleep":     Toybox,
+	"sort":      Toybox,
+	"stat":      Toybox,
+	"tail":      Toybox,
+	"tee":       Toybox,
+	"touch":     Toybox,
+	"true":      Toybox,
+	"uname":     Toybox,
+	"uniq":      Toybox,
+	"unix2dos":  Toybox,
+	"wc":        Toybox,
+	"whoami":    Toybox,
+	"xargs":     Toybox,
+	"xxd":       Toybox,
 }
 
 func init() {