Support java_sdk_library as java_libs of apex

When a java_sdk_library module is added, both impl jar and permission
xml files are packaged together.

For example, when a java_sdk_library "foo" is listed, following two
entries will be in an APEX package.

/javalibs/foo.jar
/etc/permissions/foo.xml

Bug: 145474221
Test: m com.android.cronet
      deapexer list com.android.cronet.apex

Change-Id: If5883c02255e9309f20810b1532d3fbe73bf4e95
diff --git a/apex/androidmk.go b/apex/androidmk.go
index a231a90..7d1a301 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -22,7 +22,6 @@
 
 	"android/soong/android"
 	"android/soong/cc"
-	"android/soong/java"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -119,7 +118,7 @@
 			}
 		}
 		if fi.class == javaSharedLib {
-			javaModule := fi.module.(*java.Library)
+			javaModule := fi.module.(javaLibrary)
 			// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
 			// we will have foo.jar.jar
diff --git a/apex/apex.go b/apex/apex.go
index 8a336ba..f6997bf 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -872,10 +872,16 @@
 	return af
 }
 
-func apexFileForJavaLibrary(ctx android.BaseModuleContext, java *java.Library) apexFile {
+// TODO(b/146586360): replace javaLibrary(in apex/apex.go) with java.Dependency
+type javaLibrary interface {
+	android.Module
+	java.Dependency
+}
+
+func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaLibrary) apexFile {
 	dirInApex := "javalib"
-	fileToCopy := java.DexJarFile()
-	return newApexFile(ctx, fileToCopy, java.Name(), dirInApex, javaSharedLib, java)
+	fileToCopy := lib.DexJar()
+	return newApexFile(ctx, fileToCopy, lib.Name(), dirInApex, javaSharedLib, lib)
 }
 
 func apexFileForPrebuiltJavaLibrary(ctx android.BaseModuleContext, java *java.Import) apexFile {
@@ -1022,6 +1028,21 @@
 						filesInfo = append(filesInfo, af)
 						return true // track transitive dependencies
 					}
+				} else if sdkLib, ok := child.(*java.SdkLibrary); ok {
+					af := apexFileForJavaLibrary(ctx, sdkLib)
+					if !af.Ok() {
+						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+						return false
+					}
+					filesInfo = append(filesInfo, af)
+
+					pf := sdkLib.PermissionFile()
+					if pf == nil {
+						ctx.PropertyErrorf("java_libs", "%q failed to generate permission XML", depName)
+						return false
+					}
+					filesInfo = append(filesInfo, newApexFile(ctx, pf, pf.Base(), "etc/permissions", etc, nil))
+					return true // track transitive dependencies
 				} else if javaLib, ok := child.(*java.Import); ok {
 					af := apexFileForPrebuiltJavaLibrary(ctx, javaLib)
 					if !af.Ok() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 035a553..bb64f80 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -306,6 +306,7 @@
 	java.RegisterJavaBuildComponents(ctx)
 	java.RegisterSystemModulesBuildComponents(ctx)
 	java.RegisterAppBuildComponents(ctx)
+	ctx.RegisterModuleType("java_sdk_library", java.SdkLibraryFactory)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
@@ -3252,6 +3253,44 @@
 	ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String())
 }
 
+func TestJavaSDKLibrary(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["foo"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+		}
+	`, withFiles(map[string][]byte{
+		"api/current.txt":        nil,
+		"api/removed.txt":        nil,
+		"api/system-current.txt": nil,
+		"api/system-removed.txt": nil,
+		"api/test-current.txt":   nil,
+		"api/test-removed.txt":   nil,
+	}))
+
+	// java_sdk_library installs both impl jar and permission XML
+	ensureExactContents(t, ctx, "myapex", []string{
+		"javalib/foo.jar",
+		"etc/permissions/foo.xml",
+	})
+	// Permission XML should point to the activated path of impl jar of java_sdk_library
+	genXMLCommand := ctx.ModuleForTests("foo", "android_common_myapex").Output("foo.xml").RuleParams.Command
+	ensureContains(t, genXMLCommand, `<library name="foo" file="/apex/myapex/javalib/foo.jar"`)
+}
+
 func TestRejectNonInstallableJavaLibrary(t *testing.T) {
 	testApexError(t, `"myjar" is not configured to be compiled into dex`, `
 		apex {
diff --git a/java/java.go b/java/java.go
index 4c80ba7..d0bf9d7 100644
--- a/java/java.go
+++ b/java/java.go
@@ -426,10 +426,6 @@
 	}
 }
 
-func (j *Module) DexJarFile() android.Path {
-	return j.dexJarFile
-}
-
 var _ android.OutputFileProducer = (*Module)(nil)
 
 type Dependency interface {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 32b2d1a..def2753 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -29,12 +29,31 @@
 	"github.com/google/blueprint/proptools"
 )
 
-var (
+const (
 	sdkStubsLibrarySuffix = ".stubs"
 	sdkSystemApiSuffix    = ".system"
 	sdkTestApiSuffix      = ".test"
 	sdkDocsSuffix         = ".docs"
 	sdkXmlFileSuffix      = ".xml"
+	permissionTemplate    = `<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+	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.
+-->
+<permissions>
+	<library name="%s" file="%s"/>
+</permissions>
+`
 )
 
 type stubsLibraryDependencyTag struct {
@@ -134,6 +153,8 @@
 	publicApiFilePath android.Path
 	systemApiFilePath android.Path
 	testApiFilePath   android.Path
+
+	permissionFile android.Path
 }
 
 var _ Dependency = (*SdkLibrary)(nil)
@@ -163,6 +184,10 @@
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	module.Library.GenerateAndroidBuildActions(ctx)
 
+	if module.ApexName() != "" {
+		module.buildPermissionFile(ctx)
+	}
+
 	// Record the paths to the header jars of the library (stubs and impl).
 	// When this java_sdk_library is dependened from others via "libs" property,
 	// the recorded paths will be returned depending on the link type of the caller.
@@ -198,6 +223,21 @@
 	})
 }
 
+func (module *SdkLibrary) buildPermissionFile(ctx android.ModuleContext) {
+	xmlContent := strings.ReplaceAll(fmt.Sprintf(permissionTemplate, module.BaseModuleName(), module.implPath()), "\n", "\\n")
+	permissionFile := android.PathForModuleOut(ctx, module.xmlFileName())
+
+	rule := android.NewRuleBuilder()
+	rule.Command().Text("echo -e ").Text(proptools.ShellEscape(xmlContent)).Text(">").Output(permissionFile)
+	rule.Build(pctx, ctx, "gen_permission_xml", "Generate permission")
+
+	module.permissionFile = permissionFile
+}
+
+func (module *SdkLibrary) PermissionFile() android.Path {
+	return module.permissionFile
+}
+
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := module.Library.AndroidMkEntries()
 	entries := &entriesList[0]
@@ -290,6 +330,12 @@
 
 // File path to the runtime implementation library
 func (module *SdkLibrary) implPath() string {
+	if apexName := module.ApexName(); apexName != "" {
+		// TODO(b/146468504): ApexName() is only a soong module name, not apex name.
+		// In most cases, this works fine. But when apex_name is set or override_apex is used
+		// this can be wrong.
+		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, module.implName())
+	}
 	partition := "system"
 	if module.SocSpecific() {
 		partition = "vendor"
@@ -532,31 +578,11 @@
 
 // Creates the xml file that publicizes the runtime library
 func (module *SdkLibrary) createXmlFile(mctx android.LoadHookContext) {
-	template := `
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
 
-     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.
--->
-
-<permissions>
-    <library name="%s" file="%s"/>
-</permissions>
-`
 	// genrule to generate the xml file content from the template above
 	// TODO: preserve newlines in the generate xml file. Newlines are being squashed
 	// in the ninja file. Do we need to have an external tool for this?
-	xmlContent := fmt.Sprintf(template, module.BaseModuleName(), module.implPath())
+	xmlContent := fmt.Sprintf(permissionTemplate, module.BaseModuleName(), module.implPath())
 	genruleProps := struct {
 		Name *string
 		Cmd  *string
@@ -666,10 +692,12 @@
 func (module *SdkLibrary) CreateInternalModules(mctx android.LoadHookContext) {
 	if len(module.Library.Module.properties.Srcs) == 0 {
 		mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs")
+		return
 	}
 
 	if len(module.sdkLibraryProperties.Api_packages) == 0 {
 		mctx.PropertyErrorf("api_packages", "java_sdk_library must specify api_packages")
+		return
 	}
 
 	missing_current_api := false
@@ -745,6 +773,7 @@
 func SdkLibraryFactory() android.Module {
 	module := &SdkLibrary{}
 	module.InitSdkLibraryProperties()
+	android.InitApexModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.CreateInternalModules(ctx) })
 	return module