diff options
| -rw-r--r-- | Android.bp | 1 | ||||
| -rw-r--r-- | android/apex.go | 25 | ||||
| -rw-r--r-- | android/neverallow.go | 10 | ||||
| -rw-r--r-- | android/neverallow_test.go | 28 | ||||
| -rw-r--r-- | apex/apex.go | 73 | ||||
| -rw-r--r-- | apex/apex_singleton.go | 65 | ||||
| -rw-r--r-- | apex/apex_test.go | 156 | ||||
| -rw-r--r-- | java/aar.go | 7 | ||||
| -rwxr-xr-x | java/app.go | 58 | ||||
| -rw-r--r-- | java/app_test.go | 95 | ||||
| -rw-r--r-- | java/dex.go | 5 | ||||
| -rw-r--r-- | java/dexpreopt_bootjars.go | 23 | ||||
| -rw-r--r-- | java/droiddoc.go | 13 | ||||
| -rw-r--r-- | java/java.go | 57 | ||||
| -rw-r--r-- | java/java_test.go | 212 | ||||
| -rw-r--r-- | java/sdk_library.go | 541 | ||||
| -rw-r--r-- | tradefed/autogen.go | 18 |
17 files changed, 1160 insertions, 227 deletions
diff --git a/Android.bp b/Android.bp index a81676fc2..d7c7dae52 100644 --- a/Android.bp +++ b/Android.bp @@ -489,6 +489,7 @@ bootstrap_go_package { srcs: [ "apex/androidmk.go", "apex/apex.go", + "apex/apex_singleton.go", "apex/builder.go", "apex/key.go", "apex/prebuilt.go", diff --git a/android/apex.go b/android/apex.go index ede096540..30152db29 100644 --- a/android/apex.go +++ b/android/apex.go @@ -33,6 +33,7 @@ type ApexInfo struct { ApexName string MinSdkVersion int + Updatable bool } // Extracted from ApexModule to make it easier to define custom subsets of the @@ -116,6 +117,9 @@ type ApexModule interface { // it returns 9 as string ChooseSdkVersion(versionList []string, maxSdkVersion int) (string, error) + // Tests if the module comes from an updatable APEX. + Updatable() bool + // List of APEXes that this module tests. The module has access to // the private part of the listed APEXes even when it is not included in the // APEXes. @@ -260,6 +264,10 @@ func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { } } +func (m *ApexModuleBase) Updatable() bool { + return m.ApexProperties.Info.Updatable +} + type byApexName []ApexInfo func (a byApexName) Len() int { return len(a) } @@ -413,21 +421,16 @@ type ApexModuleDepInfo struct { type DepNameToDepInfoMap map[string]ApexModuleDepInfo type ApexBundleDepsInfo struct { - minSdkVersion string - flatListPath OutputPath - fullListPath OutputPath + flatListPath OutputPath + fullListPath OutputPath } -type ApexDepsInfoIntf interface { - MinSdkVersion() string +type ApexBundleDepsInfoIntf interface { + Updatable() bool FlatListPath() Path FullListPath() Path } -func (d *ApexBundleDepsInfo) MinSdkVersion() string { - return d.minSdkVersion -} - func (d *ApexBundleDepsInfo) FlatListPath() Path { return d.flatListPath } @@ -436,14 +439,10 @@ func (d *ApexBundleDepsInfo) FullListPath() Path { return d.fullListPath } -var _ ApexDepsInfoIntf = (*ApexBundleDepsInfo)(nil) - // Generate two module out files: // 1. FullList with transitive deps and their parents in the dep graph // 2. FlatList with a flat list of transitive deps func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion string, depInfos DepNameToDepInfoMap) { - d.minSdkVersion = minSdkVersion - var fullContent strings.Builder var flatContent strings.Builder diff --git a/android/neverallow.go b/android/neverallow.go index f9fc03caa..9e075b719 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -55,6 +55,7 @@ func init() { AddNeverAllowRules(createMediaRules()...) AddNeverAllowRules(createJavaDeviceForHostRules()...) AddNeverAllowRules(createCcSdkVariantRules()...) + AddNeverAllowRules(createUncompressDexRules()...) } // Add a NeverAllow rule to the set of rules to apply. @@ -210,6 +211,15 @@ func createCcSdkVariantRules() []Rule { } } +func createUncompressDexRules() []Rule { + return []Rule{ + NeverAllow(). + NotIn("art"). + WithMatcher("uncompress_dex", isSetMatcherInstance). + Because("uncompress_dex is only allowed for certain jars for test in art."), + } +} + func neverallowMutator(ctx BottomUpMutatorContext) { m, ok := ctx.Module().(Module) if !ok { diff --git a/android/neverallow_test.go b/android/neverallow_test.go index 2fc42e31f..85c8c5908 100644 --- a/android/neverallow_test.go +++ b/android/neverallow_test.go @@ -303,6 +303,29 @@ var neverallowTests = []struct { `module "outside_whitelist": violates neverallow`, }, }, + { + name: "uncompress_dex inside art", + fs: map[string][]byte{ + "art/Android.bp": []byte(` + java_library { + name: "inside_art_libraries", + uncompress_dex: true, + }`), + }, + }, + { + name: "uncompress_dex outside art", + fs: map[string][]byte{ + "other/Android.bp": []byte(` + java_library { + name: "outside_art_libraries", + uncompress_dex: true, + }`), + }, + expectedErrors: []string{ + "module \"outside_art_libraries\": violates neverallow", + }, + }, } func TestNeverallow(t *testing.T) { @@ -396,8 +419,9 @@ func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) { } type mockJavaLibraryProperties struct { - Libs []string - Sdk_version *string + Libs []string + Sdk_version *string + Uncompress_dex *bool } type mockJavaLibraryModule struct { diff --git a/apex/apex.go b/apex/apex.go index dbbfe78af..6ae6cd959 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -92,66 +92,10 @@ func makeApexAvailableWhitelist() map[string][]string { // // Module separator // - artApexContents := []string{ - "art_cmdlineparser_headers", - "art_disassembler_headers", - "art_libartbase_headers", - "bionic_libc_platform_headers", - "core-repackaged-icu4j", - "cpp-define-generator-asm-support", - "cpp-define-generator-definitions", - "crtbegin_dynamic", - "crtbegin_dynamic1", - "crtbegin_so1", - "crtbrand", - "dex2oat_headers", - "dt_fd_forward_export", - "icu4c_extra_headers", - "javavm_headers", - "jni_platform_headers", - "libPlatformProperties", - "libadbconnection_client", - "libadbconnection_server", - "libandroidicuinit", - "libart_runtime_headers_ndk", - "libartd-disassembler", - "libdexfile_all_headers", - "libdexfile_external_headers", - "libdexfile_support", - "libdmabufinfo", - "libexpat", - "libfdlibm", - "libicui18n_headers", - "libicuuc", - "libicuuc_headers", - "libicuuc_stubdata", - "libjdwp_headers", - "liblz4", - "liblzma", - "libmeminfo", - "libnativebridge-headers", - "libnativehelper_header_only", - "libnativeloader-headers", - "libnpt_headers", - "libopenjdkjvmti_headers", - "libperfetto_client_experimental", - "libprocinfo", - "libunwind_llvm", - "libunwindstack", - "libv8", - "libv8base", - "libv8gen", - "libv8platform", - "libv8sampler", - "libv8src", - "libvixl", - "libvixld", - "libz", - "libziparchive", - "perfetto_trace_protos", + m["com.android.appsearch"] = []string{ + "icing-java-proto-lite", + "libprotobuf-java-lite", } - m["com.android.art.debug"] = artApexContents - m["com.android.art.release"] = artApexContents // // Module separator // @@ -785,6 +729,7 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) { apexBundles = []android.ApexInfo{{ ApexName: mctx.ModuleName(), MinSdkVersion: a.minSdkVersion(mctx), + Updatable: a.Updatable(), }} directDep = true } else if am, ok := mctx.Module().(android.ApexModule); ok { @@ -1828,6 +1773,12 @@ func PrettyPrintTag(tag blueprint.DependencyTag) string { return tagString } +func (a *apexBundle) Updatable() bool { + return proptools.Bool(a.properties.Updatable) +} + +var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil) + // Ensures that the dependencies are marked as available for this APEX func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { // Let's be practical. Availability for test, host, and the VNDK apex isn't important @@ -1876,7 +1827,7 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { } func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) { - if proptools.Bool(a.properties.Updatable) { + if a.Updatable() { if String(a.properties.Min_sdk_version) == "" { ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well") } @@ -2203,7 +2154,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // We don't need the optimization for updatable APEXes, as it might give false signal // to the system health when the APEXes are still bundled (b/149805758) - if proptools.Bool(a.properties.Updatable) && a.properties.ApexType == imageApex { + if a.Updatable() && a.properties.ApexType == imageApex { a.linkToSystemLib = false } diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go new file mode 100644 index 000000000..83a56a2b5 --- /dev/null +++ b/apex/apex_singleton.go @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 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. + */ + +package apex + +import ( + "github.com/google/blueprint" + + "android/soong/android" +) + +func init() { + android.RegisterSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory) +} + +type apexDepsInfoSingleton struct { + // Output file with all flatlists from updatable modules' deps-info combined + updatableFlatListsPath android.OutputPath +} + +func apexDepsInfoSingletonFactory() android.Singleton { + return &apexDepsInfoSingleton{} +} + +var combineFilesRule = pctx.AndroidStaticRule("combineFilesRule", + blueprint.RuleParams{ + Command: "cat $out.rsp | xargs cat > $out", + Rspfile: "$out.rsp", + RspfileContent: "$in", + }, +) + +func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContext) { + updatableFlatLists := android.Paths{} + ctx.VisitAllModules(func(module android.Module) { + if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok { + if path := binaryInfo.FlatListPath(); path != nil { + if binaryInfo.Updatable() { + updatableFlatLists = append(updatableFlatLists, path) + } + } + } + }) + + s.updatableFlatListsPath = android.PathForOutput(ctx, "apex", "depsinfo", "updatable-flatlists.txt") + ctx.Build(pctx, android.BuildParams{ + Rule: combineFilesRule, + Description: "Generate " + s.updatableFlatListsPath.String(), + Inputs: updatableFlatLists, + Output: s.updatableFlatListsPath, + }) +} diff --git a/apex/apex_test.go b/apex/apex_test.go index ad46c3098..a2d7a9713 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -19,6 +19,7 @@ import ( "os" "path" "reflect" + "regexp" "sort" "strings" "testing" @@ -4020,6 +4021,15 @@ func TestLegacyAndroid10Support(t *testing.T) { } } +var filesForSdkLibrary = 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, +} + func TestJavaSDKLibrary(t *testing.T) { ctx, _ := testApex(t, ` apex { @@ -4040,14 +4050,7 @@ func TestJavaSDKLibrary(t *testing.T) { api_packages: ["foo"], apex_available: [ "myapex" ], } - `, 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, - })) + `, withFiles(filesForSdkLibrary)) // java_sdk_library installs both impl jar and permission XML ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ @@ -4059,6 +4062,98 @@ func TestJavaSDKLibrary(t *testing.T) { ensureContains(t, sdkLibrary.RuleParams.Command, `<library name=\"foo\" file=\"/apex/myapex/javalib/foo.jar\"`) } +func TestJavaSDKLibrary_WithinApex(t *testing.T) { + ctx, _ := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + java_libs: ["foo", "bar"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_sdk_library { + name: "foo", + srcs: ["a.java"], + api_packages: ["foo"], + apex_available: ["myapex"], + sdk_version: "none", + system_modules: "none", + } + + java_library { + name: "bar", + srcs: ["a.java"], + libs: ["foo"], + apex_available: ["myapex"], + sdk_version: "none", + system_modules: "none", + } + `, withFiles(filesForSdkLibrary)) + + // java_sdk_library installs both impl jar and permission XML + ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + "javalib/bar.jar", + "javalib/foo.jar", + "etc/permissions/foo.xml", + }) + + // The bar library should depend on the implementation jar. + barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac") + if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) { + t.Errorf("expected %q, found %#q", expected, actual) + } +} + +func TestJavaSDKLibrary_CrossBoundary(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"], + apex_available: ["myapex"], + sdk_version: "none", + system_modules: "none", + } + + java_library { + name: "bar", + srcs: ["a.java"], + libs: ["foo"], + sdk_version: "none", + system_modules: "none", + } + `, withFiles(filesForSdkLibrary)) + + // java_sdk_library installs both impl jar and permission XML + ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + "javalib/foo.jar", + "etc/permissions/foo.xml", + }) + + // The bar library should depend on the stubs jar. + barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac") + if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) { + t.Errorf("expected %q, found %#q", expected, actual) + } +} + func TestCompatConfig(t *testing.T) { ctx, _ := testApex(t, ` apex { @@ -4342,6 +4437,13 @@ func testNoUpdatableJarsInBootImage(t *testing.T, errmsg, bp string, transformDe "system/sepolicy/apex/some-updatable-apex-file_contexts", ], } + + filegroup { + name: "some-non-updatable-apex-file_contexts", + srcs: [ + "system/sepolicy/apex/some-non-updatable-apex-file_contexts", + ], + } ` bp += cc.GatherRequiredDepsForTest(android.Android) bp += java.GatherRequiredDepsForTest() @@ -4354,6 +4456,7 @@ func testNoUpdatableJarsInBootImage(t *testing.T, errmsg, bp string, transformDe "apex_manifest.json": nil, "AndroidManifest.xml": nil, "system/sepolicy/apex/some-updatable-apex-file_contexts": nil, + "system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil, "system/sepolicy/apex/com.android.art.something-file_contexts": nil, "framework/aidl/a.aidl": nil, } @@ -4425,6 +4528,14 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) { } java_library { + name: "some-non-updatable-apex-lib", + srcs: ["a.java"], + apex_available: [ + "some-non-updatable-apex", + ], + } + + java_library { name: "some-platform-lib", srcs: ["a.java"], sdk_version: "current", @@ -4445,16 +4556,30 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) { name: "some-updatable-apex", key: "some-updatable-apex.key", java_libs: ["some-updatable-apex-lib"], + updatable: true, + min_sdk_version: "current", + } + + apex { + name: "some-non-updatable-apex", + key: "some-non-updatable-apex.key", + java_libs: ["some-non-updatable-apex-lib"], } apex_key { name: "some-updatable-apex.key", } + apex_key { + name: "some-non-updatable-apex.key", + } + apex { name: "com.android.art.something", key: "com.android.art.something.key", java_libs: ["some-art-lib"], + updatable: true, + min_sdk_version: "current", } apex_key { @@ -4485,6 +4610,13 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) { } testNoUpdatableJarsInBootImage(t, error, bp, transform) + // non-updatable jar from some other apex in the ART boot image => error + error = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image" + transform = func(config *dexpreopt.GlobalConfig) { + config.ArtApexJars = []string{"some-non-updatable-apex-lib"} + } + testNoUpdatableJarsInBootImage(t, error, bp, transform) + // updatable jar from some other apex in the framework boot image => error error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image" transform = func(config *dexpreopt.GlobalConfig) { @@ -4492,6 +4624,12 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) { } testNoUpdatableJarsInBootImage(t, error, bp, transform) + // non-updatable jar from some other apex in the framework boot image => ok + transform = func(config *dexpreopt.GlobalConfig) { + config.BootJars = []string{"some-non-updatable-apex-lib"} + } + testNoUpdatableJarsInBootImage(t, "", bp, transform) + // nonexistent jar in the ART boot image => error error = "failed to find a dex jar path for module 'nonexistent'" transform = func(config *dexpreopt.GlobalConfig) { @@ -4507,7 +4645,7 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) { testNoUpdatableJarsInBootImage(t, error, bp, transform) // platform jar in the ART boot image => error - error = "module 'some-platform-lib' is part of the platform and not allowed in the ART boot image" + error = "module 'some-platform-lib' is not allowed in the ART boot image" transform = func(config *dexpreopt.GlobalConfig) { config.ArtApexJars = []string{"some-platform-lib"} } diff --git a/java/aar.go b/java/aar.go index 0e103f248..c8daf835b 100644 --- a/java/aar.go +++ b/java/aar.go @@ -379,8 +379,11 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati sharedLibs = append(sharedLibs, exportPackage) } - if _, ok := module.(SdkLibraryDependency); ok { - sdkLibraries = append(sdkLibraries, ctx.OtherModuleName(module)) + // If the module is (or possibly could be) a component of a java_sdk_library + // (including the java_sdk_library) itself then append any implicit sdk library + // names to the list of sdk libraries to be added to the manifest. + if component, ok := module.(SdkLibraryComponentDependency); ok { + sdkLibraries = append(sdkLibraries, component.OptionalImplicitSdkLibrary()...) } case frameworkResTag: diff --git a/java/app.go b/java/app.go index 86cf7471d..e44b87d92 100755 --- a/java/app.go +++ b/java/app.go @@ -414,7 +414,7 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { } func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { - if Bool(a.appProperties.Updatable) { + if a.Updatable() { if !a.sdkVersion().stable() { ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.sdkVersion()) } @@ -540,16 +540,17 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { installDir = filepath.Join("app", a.installApkName) } a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk") - a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) - + if a.deviceProperties.Uncompress_dex == nil { + // If the value was not force-set by the user, use reasonable default based on the module. + a.deviceProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx)) + } + a.dexpreopter.uncompressedDex = *a.deviceProperties.Uncompress_dex a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx) a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx) a.dexpreopter.manifestFile = a.mergedManifestFile - a.deviceProperties.UncompressDex = a.dexpreopter.uncompressedDex - if ctx.ModuleName() != "framework-res" { a.Module.compile(ctx, a.aaptSrcJar) } @@ -879,6 +880,10 @@ func (a *AndroidApp) buildAppDependencyInfo(ctx android.ModuleContext) { a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(), depsInfo) } +func (a *AndroidApp) Updatable() bool { + return Bool(a.appProperties.Updatable) || a.ApexModuleBase.Updatable() +} + func (a *AndroidApp) getCertString(ctx android.BaseModuleContext) string { certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName()) if overridden { @@ -980,6 +985,7 @@ func (a *AndroidTest) InstallInTestcases() bool { } func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { + var configs []tradefed.Config if a.appTestProperties.Instrumentation_target_package != nil { a.additionalAaptFlags = append(a.additionalAaptFlags, "--rename-instrumentation-target-package "+*a.appTestProperties.Instrumentation_target_package) @@ -992,8 +998,12 @@ func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { } a.generateAndroidBuildActions(ctx) + for _, module := range a.testProperties.Test_mainline_modules { + configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module}) + } + testConfig := tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, - a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config) + a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config, configs) a.testConfig = a.FixTestConfig(ctx, testConfig) a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) } @@ -1230,6 +1240,8 @@ type AndroidAppImport struct { usesLibrary usesLibrary + preprocessed bool + installPath android.InstallPath } @@ -1322,7 +1334,7 @@ func (a *AndroidAppImport) uncompressEmbeddedJniLibs( ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) { // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing // with them may invalidate pre-existing signature data. - if ctx.InstallInTestcases() && Bool(a.properties.Presigned) { + if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) { ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, Output: outputPath, @@ -1343,7 +1355,7 @@ func (a *AndroidAppImport) uncompressEmbeddedJniLibs( // Returns whether this module should have the dex file stored uncompressed in the APK. func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool { - if ctx.Config().UnbundledBuild() { + if ctx.Config().UnbundledBuild() || a.preprocessed { return false } @@ -1435,9 +1447,13 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk") - // Sign or align the package // TODO: Handle EXTERNAL - if !Bool(a.properties.Presigned) { + + // Sign or align the package if package has not been preprocessed + if a.preprocessed { + a.outputFile = srcApk + a.certificate = presignedCertificate + } else if !Bool(a.properties.Presigned) { // If the certificate property is empty at this point, default_dev_cert must be set to true. // Which makes processMainCert's behavior for the empty cert string WAI. certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx) @@ -1566,21 +1582,31 @@ func AndroidAppImportFactory() android.Module { module.processVariants(ctx) }) - InitJavaModule(module, android.DeviceSupported) + android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk") return module } +type androidTestImportProperties struct { + // Whether the prebuilt apk can be installed without additional processing. Default is false. + Preprocessed *bool +} + type AndroidTestImport struct { AndroidAppImport testProperties testProperties + testImportProperties androidTestImportProperties + data android.Paths } func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { + a.preprocessed = Bool(a.testImportProperties.Preprocessed) + a.generateAndroidBuildActions(ctx) a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) @@ -1598,6 +1624,7 @@ func AndroidTestImportFactory() android.Module { module.AddProperties(&module.dexpreoptProperties) module.AddProperties(&module.usesLibrary.usesLibraryProperties) module.AddProperties(&module.testProperties) + module.AddProperties(&module.testImportProperties) module.populateAllVariantStructs() android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.processVariants(ctx) @@ -1632,6 +1659,9 @@ type RuntimeResourceOverlayProperties struct { // module name in the form ":module". Certificate *string + // Name of the signing certificate lineage file. + Lineage *string + // optional theme name. If specified, the overlay package will be applied // only when the ro.boot.vendor.overlay.theme system property is set to the same value. Theme *string @@ -1697,7 +1727,11 @@ func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleC _, certificates := collectAppDeps(ctx, r, false, false) certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx) signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk") - SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, nil) + var lineageFile android.Path + if lineage := String(r.properties.Lineage); lineage != "" { + lineageFile = android.PathForModuleSrc(ctx, lineage) + } + SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile) r.certificate = certificates[0] r.outputFile = signed diff --git a/java/app_test.go b/java/app_test.go index c731a1713..8afd97d9e 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -2308,6 +2308,45 @@ func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) { if jniRule != android.Cp.String() { t.Errorf("Unexpected JNI uncompress rule: " + jniRule) } + if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil { + t.Errorf("Presigned test apk should be aligned") + } +} + +func TestAndroidTestImport_Preprocessed(t *testing.T) { + ctx, _ := testJava(t, ` + android_test_import { + name: "foo", + apk: "prebuilts/apk/app.apk", + presigned: true, + preprocessed: true, + } + + android_test_import { + name: "foo_cert", + apk: "prebuilts/apk/app.apk", + certificate: "cert/new_cert", + preprocessed: true, + } + `) + + testModules := []string{"foo", "foo_cert"} + for _, m := range testModules { + apkName := m + ".apk" + variant := ctx.ModuleForTests(m, "android_common") + jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String() + if jniRule != android.Cp.String() { + t.Errorf("Unexpected JNI uncompress rule: " + jniRule) + } + + // Make sure signing and aligning were skipped. + if variant.MaybeOutput("signed/"+apkName).Rule != nil { + t.Errorf("signing rule shouldn't be included for preprocessed.") + } + if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil { + t.Errorf("aligning rule shouldn't be for preprocessed") + } + } } func TestStl(t *testing.T) { @@ -2383,6 +2422,20 @@ func TestUsesLibraries(t *testing.T) { } java_sdk_library { + name: "qux", + srcs: ["a.java"], + api_packages: ["qux"], + sdk_version: "current", + } + + java_sdk_library { + name: "quuz", + srcs: ["a.java"], + api_packages: ["quuz"], + sdk_version: "current", + } + + java_sdk_library { name: "bar", srcs: ["a.java"], api_packages: ["bar"], @@ -2392,6 +2445,7 @@ func TestUsesLibraries(t *testing.T) { android_app { name: "app", srcs: ["a.java"], + libs: ["qux", "quuz.stubs"], uses_libs: ["foo"], sdk_version: "current", optional_uses_libs: [ @@ -2422,6 +2476,15 @@ func TestUsesLibraries(t *testing.T) { app := ctx.ModuleForTests("app", "android_common") prebuilt := ctx.ModuleForTests("prebuilt", "android_common") + // Test that implicit dependencies on java_sdk_library instances are passed to the manifest. + manifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"] + if w := "--uses-library qux"; !strings.Contains(manifestFixerArgs, w) { + t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs) + } + if w := "--uses-library quuz"; !strings.Contains(manifestFixerArgs, w) { + t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs) + } + // Test that all libraries are verified cmd := app.Rule("verify_uses_libraries").RuleParams.Command if w := "--uses-library foo"; !strings.Contains(cmd, w) { @@ -2682,6 +2745,32 @@ func TestUncompressDex(t *testing.T) { uncompressedPlatform: true, uncompressedUnbundled: true, }, + { + name: "normal_uncompress_dex_true", + bp: ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + uncompress_dex: true, + } + `, + uncompressedPlatform: true, + uncompressedUnbundled: true, + }, + { + name: "normal_uncompress_dex_false", + bp: ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + uncompress_dex: false, + } + `, + uncompressedPlatform: false, + uncompressedUnbundled: false, + }, } test := func(t *testing.T, bp string, want bool, unbundled bool) { @@ -2745,6 +2834,7 @@ func TestRuntimeResourceOverlay(t *testing.T) { runtime_resource_overlay { name: "foo", certificate: "platform", + lineage: "lineage.bin", product_specific: true, static_libs: ["bar"], resource_libs: ["baz"], @@ -2799,6 +2889,11 @@ func TestRuntimeResourceOverlay(t *testing.T) { // Check cert signing flag. signedApk := m.Output("signed/foo.apk") + lineageFlag := signedApk.Args["flags"] + expectedLineageFlag := "--lineage lineage.bin" + if expectedLineageFlag != lineageFlag { + t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag) + } signingFlag := signedApk.Args["certificates"] expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8" if expected != signingFlag { diff --git a/java/dex.go b/java/dex.go index 6afdb6dc4..62f17c6d8 100644 --- a/java/dex.go +++ b/java/dex.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" "android/soong/android" ) @@ -177,7 +178,7 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, outDir := android.PathForModuleOut(ctx, "dex") zipFlags := "--ignore_missing_files" - if j.deviceProperties.UncompressDex { + if proptools.Bool(j.deviceProperties.Uncompress_dex) { zipFlags += " -L 0" } @@ -214,7 +215,7 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, }, }) } - if j.deviceProperties.UncompressDex { + if proptools.Bool(j.deviceProperties.Uncompress_dex) { alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName) TransformZipAlign(ctx, alignedJavalibJar, javalibJar) javalibJar = alignedJavalibJar diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 1b2666708..b518e9c0d 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -264,27 +264,28 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul // Check that this module satisfies constraints for a particular boot image. apex, isApexModule := module.(android.ApexModule) + fromUpdatableApex := isApexModule && apex.Updatable() if image.name == artBootImageName { if isApexModule && strings.HasPrefix(apex.ApexName(), "com.android.art.") { - // ok, found the jar in the ART apex - } else if isApexModule && !apex.IsForPlatform() { - // this jar is part of an updatable apex other than ART, fail immediately - ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the ART boot image", name, apex.ApexName()) + // ok: found the jar in the ART apex } else if isApexModule && apex.IsForPlatform() && Bool(module.(*Library).deviceProperties.Hostdex) { - // this is a special "hostdex" variant, skip it and resume search + // exception (skip and continue): special "hostdex" platform variant return -1, nil } else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") { - // this is Jacoco platform variant for a coverage build, skip it and resume search + // exception (skip and continue): Jacoco platform variant for a coverage build return -1, nil + } else if fromUpdatableApex { + // error: this jar is part of an updatable apex other than ART + ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the ART boot image", name, apex.ApexName()) } else { - // this (installable) jar is part of the platform, fail immediately - ctx.Errorf("module '%s' is part of the platform and not allowed in the ART boot image", name) + // error: this jar is part of the platform or a non-updatable apex + ctx.Errorf("module '%s' is not allowed in the ART boot image", name) } } else if image.name == frameworkBootImageName { - if !isApexModule || apex.IsForPlatform() { - // ok, this jar is part of the platform + if !fromUpdatableApex { + // ok: this jar is part of the platform or a non-updatable apex } else { - // this jar is part of an updatable apex, fail immediately + // error: this jar is part of an updatable apex ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the framework boot image", name, apex.ApexName()) } } else { diff --git a/java/droiddoc.go b/java/droiddoc.go index 4b4a245e7..cf4c892f9 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -350,11 +350,16 @@ type ApiFilePath interface { ApiFilePath() android.Path } +type ApiStubsSrcProvider interface { + StubsSrcJar() android.Path +} + // Provider of information about API stubs, used by java_sdk_library. type ApiStubsProvider interface { ApiFilePath RemovedApiFilePath() android.Path - StubsSrcJar() android.Path + + ApiStubsSrcProvider } // @@ -540,7 +545,7 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { case libTag: switch dep := module.(type) { case SdkLibraryDependency: - deps.classpath = append(deps.classpath, dep.SdkImplementationJars(ctx, j.sdkVersion())...) + deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) case Dependency: deps.classpath = append(deps.classpath, dep.HeaderJars()...) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) @@ -1912,6 +1917,10 @@ func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) { } } +func (d *PrebuiltStubsSources) StubsSrcJar() android.Path { + return d.stubsSrcJar +} + func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") diff --git a/java/java.go b/java/java.go index 0c5ef5db8..81f98c0d5 100644 --- a/java/java.go +++ b/java/java.go @@ -342,8 +342,13 @@ type CompilerDeviceProperties struct { // set the name of the output Stem *string - UncompressDex bool `blueprint:"mutated"` - IsSDKLibrary bool `blueprint:"mutated"` + // Keep the data uncompressed. We always need uncompressed dex for execution, + // so this might actually save space by avoiding storing the same data twice. + // This defaults to reasonable value based on module and should not be set. + // It exists only to support ART tests. + Uncompress_dex *bool + + IsSDKLibrary bool `blueprint:"mutated"` // If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file. // Defaults to false. @@ -355,7 +360,17 @@ func (me *CompilerDeviceProperties) EffectiveOptimizeEnabled() bool { } // Functionality common to Module and Import +// +// It is embedded in Module so its functionality can be used by methods in Module +// but it is currently only initialized by Import and Library. type embeddableInModuleAndImport struct { + + // Functionality related to this being used as a component of a java_sdk_library. + EmbeddableSdkLibraryComponent +} + +func (e *embeddableInModuleAndImport) initModuleAndImport(moduleBase *android.ModuleBase) { + e.initSdkLibraryComponent(moduleBase) } // Module/Import's DepIsInSameApex(...) delegates to this method. @@ -496,11 +511,6 @@ type Dependency interface { JacocoReportClassesFile() android.Path } -type SdkLibraryDependency interface { - SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths - SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths -} - type xref interface { XrefJavaFiles() android.Paths } @@ -935,6 +945,12 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { } } + // If this is a component library (stubs, etc.) for a java_sdk_library then + // add the name of that java_sdk_library to the exported sdk libs to make sure + // that, if necessary, a <uses-library> element for that java_sdk_library is + // added to the Android manifest. + j.exportedSdkLibs = append(j.exportedSdkLibs, j.OptionalImplicitSdkLibrary()...) + ctx.VisitDirectDeps(func(module android.Module) { otherName := ctx.OtherModuleName(module) tag := ctx.OtherModuleDependencyTag(module) @@ -954,7 +970,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case libTag: deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) // names of sdk libs that are directly depended are exported - j.exportedSdkLibs = append(j.exportedSdkLibs, otherName) + j.exportedSdkLibs = append(j.exportedSdkLibs, dep.OptionalImplicitSdkLibrary()...) case staticLibTag: ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) } @@ -1565,7 +1581,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // Hidden API CSV generation and dex encoding dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile, - j.deviceProperties.UncompressDex) + proptools.Bool(j.deviceProperties.Uncompress_dex)) // merge dex jar with resources if necessary if j.resourceJar != nil { @@ -1573,7 +1589,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName) TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{}, false, nil, nil) - if j.deviceProperties.UncompressDex { + if *j.deviceProperties.Uncompress_dex { combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName) TransformZipAlign(ctx, combinedAlignedJar, combinedJar) dexOutputFile = combinedAlignedJar @@ -1850,8 +1866,11 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.checkSdkVersions(ctx) j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary - j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter) - j.deviceProperties.UncompressDex = j.dexpreopter.uncompressedDex + if j.deviceProperties.Uncompress_dex == nil { + // If the value was not force-set by the user, use reasonable default based on the module. + j.deviceProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter)) + } + j.dexpreopter.uncompressedDex = *j.deviceProperties.Uncompress_dex j.compile(ctx, nil) exclusivelyForApex := android.InAnyApex(ctx.ModuleName()) && !j.IsForPlatform() @@ -1995,6 +2014,8 @@ func LibraryFactory() android.Module { &module.Module.protoProperties, &module.libraryProperties) + module.initModuleAndImport(&module.ModuleBase) + android.InitApexModule(module) android.InitSdkAwareModule(module) InitJavaModule(module, android.HostAndDeviceSupported) @@ -2049,6 +2070,10 @@ type testProperties struct { // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true // explicitly. Auto_gen_config *bool + + // Add parameterized mainline modules to auto generated test config. The options will be + // handled by TradeFed to do downloading and installing the specified modules on the device. + Test_mainline_modules []string } type testHelperLibraryProperties struct { @@ -2456,6 +2481,12 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } j.combinedClasspathFile = outputFile + // If this is a component library (impl, stubs, etc.) for a java_sdk_library then + // add the name of that java_sdk_library to the exported sdk libs to make sure + // that, if necessary, a <uses-library> element for that java_sdk_library is + // added to the Android manifest. + j.exportedSdkLibs = append(j.exportedSdkLibs, j.OptionalImplicitSdkLibrary()...) + ctx.VisitDirectDeps(func(module android.Module) { otherName := ctx.OtherModuleName(module) tag := ctx.OtherModuleDependencyTag(module) @@ -2572,6 +2603,8 @@ func ImportFactory() android.Module { module.AddProperties(&module.properties) + module.initModuleAndImport(&module.ModuleBase) + android.InitPrebuiltModule(module, &module.properties.Jars) android.InitApexModule(module) android.InitSdkAwareModule(module) diff --git a/java/java_test.go b/java/java_test.go index 01ddccfc1..385fe6afd 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -19,6 +19,8 @@ import ( "os" "path/filepath" "reflect" + "regexp" + "sort" "strconv" "strings" "testing" @@ -575,6 +577,7 @@ func TestJavaSdkLibraryImport(t *testing.T) { }, test: { jars: ["c.jar"], + stub_srcs: ["c.java"], }, } `) @@ -1152,13 +1155,42 @@ func TestJavaSdkLibrary(t *testing.T) { java_library { name: "baz", srcs: ["c.java"], - libs: ["foo", "bar"], + libs: ["foo", "bar.stubs"], sdk_version: "system_current", } + java_sdk_library { + name: "barney", + srcs: ["c.java"], + api_only: true, + } + java_sdk_library { + name: "betty", + srcs: ["c.java"], + shared_library: false, + } + java_sdk_library_import { + name: "quuz", + public: { + jars: ["c.jar"], + }, + } + java_sdk_library_import { + name: "fred", + public: { + jars: ["b.jar"], + }, + } + java_sdk_library_import { + name: "wilma", + public: { + jars: ["b.jar"], + }, + shared_library: false, + } java_library { name: "qux", srcs: ["c.java"], - libs: ["baz"], + libs: ["baz", "fred", "quuz.stubs", "wilma", "barney", "betty"], sdk_version: "system_current", } java_library { @@ -1223,12 +1255,145 @@ func TestJavaSdkLibrary(t *testing.T) { qux := ctx.ModuleForTests("qux", "android_common") if quxLib, ok := qux.Module().(*Library); ok { sdkLibs := quxLib.ExportedSdkLibs() - if len(sdkLibs) != 2 || !android.InList("foo", sdkLibs) || !android.InList("bar", sdkLibs) { - t.Errorf("qux should export \"foo\" and \"bar\" but exports %v", sdkLibs) + sort.Strings(sdkLibs) + if w := []string{"bar", "foo", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) { + t.Errorf("qux should export %q but exports %q", w, sdkLibs) } } } +func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) { + ctx, _ := testJava(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + api_only: true, + public: { + enabled: true, + }, + } + + java_library { + name: "bar", + srcs: ["b.java"], + libs: ["foo"], + } + `) + + // The bar library should depend on the stubs jar. + barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac") + if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) { + t.Errorf("expected %q, found %#q", expected, actual) + } +} + +func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { + testJava(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + api_packages: ["foo"], + public: { + enabled: true, + }, + } + + java_library { + name: "bar", + srcs: ["b.java", ":foo{.public.stubs.source}"], + } + `) +} + +func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) { + testJavaError(t, `"foo" does not provide api scope system`, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + api_packages: ["foo"], + public: { + enabled: true, + }, + } + + java_library { + name: "bar", + srcs: ["b.java", ":foo{.system.stubs.source}"], + } + `) +} + +func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) { + testJava(t, ` + java_sdk_library_import { + name: "foo", + public: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "api/current.txt", + removed_api: "api/removed.txt", + }, + } + + java_library { + name: "bar", + srcs: [":foo{.public.stubs.source}"], + java_resources: [ + ":foo{.public.api.txt}", + ":foo{.public.removed-api.txt}", + ], + } + `) +} + +func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) { + bp := ` + java_sdk_library_import { + name: "foo", + public: { + jars: ["a.jar"], + }, + } + ` + + t.Run("stubs.source", func(t *testing.T) { + testJavaError(t, `stubs.source not available for api scope public`, bp+` + java_library { + name: "bar", + srcs: [":foo{.public.stubs.source}"], + java_resources: [ + ":foo{.public.api.txt}", + ":foo{.public.removed-api.txt}", + ], + } + `) + }) + + t.Run("api.txt", func(t *testing.T) { + testJavaError(t, `api.txt not available for api scope public`, bp+` + java_library { + name: "bar", + srcs: ["a.java"], + java_resources: [ + ":foo{.public.api.txt}", + ], + } + `) + }) + + t.Run("removed-api.txt", func(t *testing.T) { + testJavaError(t, `removed-api.txt not available for api scope public`, bp+` + java_library { + name: "bar", + srcs: ["a.java"], + java_resources: [ + ":foo{.public.removed-api.txt}", + ], + } + `) + }) +} + func TestJavaSdkLibrary_InvalidScopes(t *testing.T) { testJavaError(t, `module "foo": enabled api scope "system" depends on disabled scope "public"`, ` java_sdk_library { @@ -1261,6 +1426,45 @@ func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) { `) } +func TestJavaSdkLibrary_MissingScope(t *testing.T) { + testJavaError(t, `requires api scope module-lib from foo but it only has \[\] available`, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + public: { + enabled: false, + }, + } + + java_library { + name: "baz", + srcs: ["a.java"], + libs: ["foo"], + sdk_version: "module_current", + } + `) +} + +func TestJavaSdkLibrary_FallbackScope(t *testing.T) { + testJava(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + system: { + enabled: true, + }, + } + + java_library { + name: "baz", + srcs: ["a.java"], + libs: ["foo"], + // foo does not have module-lib scope so it should fallback to system + sdk_version: "module_current", + } + `) +} + var compilerFlagsTestCases = []struct { in string out bool diff --git a/java/sdk_library.go b/java/sdk_library.go index 94f38f558..fdc9fee78 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -19,6 +19,7 @@ import ( "path" "path/filepath" "reflect" + "regexp" "sort" "strings" "sync" @@ -145,6 +146,8 @@ type apiScope struct { // Initialize a scope, creating and adding appropriate dependency tags func initApiScope(scope *apiScope) *apiScope { name := scope.name + scopeByName[name] = scope + allScopeNames = append(allScopeNames, name) scope.propertyName = strings.ReplaceAll(name, "-", "_") scope.fieldName = proptools.FieldNameForProperty(scope.propertyName) scope.stubsTag = scopeDependencyTag{ @@ -217,6 +220,8 @@ func (scopes apiScopes) Strings(accessor func(*apiScope) string) []string { } var ( + scopeByName = make(map[string]*apiScope) + allScopeNames []string apiScopePublic = initApiScope(&apiScope{ name: "public", @@ -343,6 +348,10 @@ type ApiScopeProperties struct { } type sdkLibraryProperties struct { + // Visibility for impl library module. If not specified then defaults to the + // visibility property. + Impl_library_visibility []string + // Visibility for stubs library modules. If not specified then defaults to the // visibility property. Stubs_library_visibility []string @@ -367,7 +376,10 @@ type sdkLibraryProperties struct { // Defaults to "api". Api_dir *string - // If set to true there is no runtime library. + // Determines whether a runtime implementation library is built; defaults to false. + // + // If true then it also prevents the module from being used as a shared module, i.e. + // it is as is shared_library: false, was set. Api_only *bool // local files that are used within user customized droiddoc options. @@ -433,12 +445,30 @@ type sdkLibraryProperties struct { //Html_doc *bool } +// Paths to outputs from java_sdk_library and java_sdk_library_import. +// +// Fields that are android.Paths are always set (during GenerateAndroidBuildActions). +// OptionalPaths are always set by java_sdk_library but may not be set by +// java_sdk_library_import as not all instances provide that information. type scopePaths struct { - stubsHeaderPath android.Paths - stubsImplPath android.Paths - currentApiFilePath android.Path - removedApiFilePath android.Path - stubsSrcJar android.Path + // The path (represented as Paths for convenience when returning) to the stubs header jar. + // + // That is the jar that is created by turbine. + stubsHeaderPath android.Paths + + // The path (represented as Paths for convenience when returning) to the stubs implementation jar. + // + // This is not the implementation jar, it still only contains stubs. + stubsImplPath android.Paths + + // The API specification file, e.g. system_current.txt. + currentApiFilePath android.OptionalPath + + // The specification of API elements removed since the last release. + removedApiFilePath android.OptionalPath + + // The stubs source jar. + stubsSrcJar android.OptionalPath } func (paths *scopePaths) extractStubsLibraryInfoFromDependency(dep android.Module) error { @@ -460,9 +490,18 @@ func (paths *scopePaths) treatDepAsApiStubsProvider(dep android.Module, action f } } +func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider)) error { + if apiStubsProvider, ok := dep.(ApiStubsSrcProvider); ok { + action(apiStubsProvider) + return nil + } else { + return fmt.Errorf("expected module that implements ApiStubsSrcProvider, e.g. droidstubs") + } +} + func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) { - paths.currentApiFilePath = provider.ApiFilePath() - paths.removedApiFilePath = provider.RemovedApiFilePath() + paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath()) + paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath()) } func (paths *scopePaths) extractApiInfoFromDep(dep android.Module) error { @@ -471,12 +510,12 @@ func (paths *scopePaths) extractApiInfoFromDep(dep android.Module) error { }) } -func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsProvider) { - paths.stubsSrcJar = provider.StubsSrcJar() +func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider) { + paths.stubsSrcJar = android.OptionalPathForPath(provider.StubsSrcJar()) } func (paths *scopePaths) extractStubsSourceInfoFromDep(dep android.Module) error { - return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) { + return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) { paths.extractStubsSourceInfoFromApiStubsProviders(provider) }) } @@ -500,6 +539,13 @@ type commonToSdkLibraryAndImportProperties struct { // // TODO(b/155480189) - Remove once naming inconsistencies have been resolved. Naming_scheme *string + + // Specifies whether this module can be used as an Android shared library; defaults + // to true. + // + // An Android shared library is one that can be referenced in a <uses-library> element + // in an AndroidManifest.xml. + Shared_library *bool } // Common code between sdk library and sdk library import @@ -510,17 +556,23 @@ type commonToSdkLibraryAndImport struct { namingScheme sdkLibraryComponentNamingScheme - commonProperties commonToSdkLibraryAndImportProperties + commonSdkLibraryProperties commonToSdkLibraryAndImportProperties + + // Functionality related to this being used as a component of a java_sdk_library. + EmbeddableSdkLibraryComponent } func (c *commonToSdkLibraryAndImport) initCommon(moduleBase *android.ModuleBase) { c.moduleBase = moduleBase - moduleBase.AddProperties(&c.commonProperties) + moduleBase.AddProperties(&c.commonSdkLibraryProperties) + + // Initialize this as an sdk library component. + c.initSdkLibraryComponent(moduleBase) } func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool { - schemeProperty := proptools.StringDefault(c.commonProperties.Naming_scheme, "default") + schemeProperty := proptools.StringDefault(c.commonSdkLibraryProperties.Naming_scheme, "default") switch schemeProperty { case "default": c.namingScheme = &defaultNamingScheme{} @@ -531,6 +583,12 @@ func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android return false } + // Only track this sdk library if this can be used as a shared library. + if c.sharedLibrary() { + // Use the name specified in the module definition as the owner. + c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName()) + } + return true } @@ -551,7 +609,83 @@ func (c *commonToSdkLibraryAndImport) apiModuleName(apiScope *apiScope) string { return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName()) } -func (c *commonToSdkLibraryAndImport) getScopePaths(scope *apiScope) *scopePaths { +// The component names for different outputs of the java_sdk_library. +// +// They are similar to the names used for the child modules it creates +const ( + stubsSourceComponentName = "stubs.source" + + apiTxtComponentName = "api.txt" + + removedApiTxtComponentName = "removed-api.txt" +) + +// A regular expression to match tags that reference a specific stubs component. +// +// It will only match if given a valid scope and a valid component. It is verfy strict +// to ensure it does not accidentally match a similar looking tag that should be processed +// by the embedded Library. +var tagSplitter = func() *regexp.Regexp { + // Given a list of literal string items returns a regular expression that will + // match any one of the items. + choice := func(items ...string) string { + return `\Q` + strings.Join(items, `\E|\Q`) + `\E` + } + + // Regular expression to match one of the scopes. + scopesRegexp := choice(allScopeNames...) + + // Regular expression to match one of the components. + componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName) + + // Regular expression to match any combination of one scope and one component. + return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp)) +}() + +// For OutputFileProducer interface +// +// .<scope>.stubs.source +// .<scope>.api.txt +// .<scope>.removed-api.txt +func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) { + if groups := tagSplitter.FindStringSubmatch(tag); groups != nil { + scopeName := groups[1] + component := groups[2] + + if scope, ok := scopeByName[scopeName]; ok { + paths := c.findScopePaths(scope) + if paths == nil { + return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName) + } + + switch component { + case stubsSourceComponentName: + if paths.stubsSrcJar.Valid() { + return android.Paths{paths.stubsSrcJar.Path()}, nil + } + + case apiTxtComponentName: + if paths.currentApiFilePath.Valid() { + return android.Paths{paths.currentApiFilePath.Path()}, nil + } + + case removedApiTxtComponentName: + if paths.removedApiFilePath.Valid() { + return android.Paths{paths.removedApiFilePath.Path()}, nil + } + } + + return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName) + } else { + return nil, fmt.Errorf("unknown scope %s in %s", scope, tag) + } + + } else { + return nil, nil + } +} + +func (c *commonToSdkLibraryAndImport) getScopePathsCreateIfNeeded(scope *apiScope) *scopePaths { if c.scopePaths == nil { c.scopePaths = make(map[*apiScope]*scopePaths) } @@ -564,6 +698,147 @@ func (c *commonToSdkLibraryAndImport) getScopePaths(scope *apiScope) *scopePaths return paths } +func (c *commonToSdkLibraryAndImport) findScopePaths(scope *apiScope) *scopePaths { + if c.scopePaths == nil { + return nil + } + + return c.scopePaths[scope] +} + +// If this does not support the requested api scope then find the closest available +// scope it does support. Returns nil if no such scope is available. +func (c *commonToSdkLibraryAndImport) findClosestScopePath(scope *apiScope) *scopePaths { + for s := scope; s != nil; s = s.extends { + if paths := c.findScopePaths(s); paths != nil { + return paths + } + } + + // This should never happen outside tests as public should be the base scope for every + // scope and is enabled by default. + return nil +} + +func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths { + + // If a specific numeric version has been requested then use prebuilt versions of the sdk. + if sdkVersion.version.isNumbered() { + return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion) + } + + var apiScope *apiScope + switch sdkVersion.kind { + case sdkSystem: + apiScope = apiScopeSystem + case sdkModule: + apiScope = apiScopeModuleLib + case sdkTest: + apiScope = apiScopeTest + default: + apiScope = apiScopePublic + } + + paths := c.findClosestScopePath(apiScope) + if paths == nil { + var scopes []string + for _, s := range allApiScopes { + if c.findScopePaths(s) != nil { + scopes = append(scopes, s.name) + } + } + ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes) + return nil + } + + return paths.stubsHeaderPath +} + +func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() interface{} { + componentProps := &struct { + SdkLibraryToImplicitlyTrack *string + }{} + + if c.sharedLibrary() { + // Mark the stubs library as being components of this java_sdk_library so that + // any app that includes code which depends (directly or indirectly) on the stubs + // library will have the appropriate <uses-library> invocation inserted into its + // manifest if necessary. + componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName()) + } + + return componentProps +} + +// Check if this can be used as a shared library. +func (c *commonToSdkLibraryAndImport) sharedLibrary() bool { + return proptools.BoolDefault(c.commonSdkLibraryProperties.Shared_library, true) +} + +// Properties related to the use of a module as an component of a java_sdk_library. +type SdkLibraryComponentProperties struct { + + // The name of the java_sdk_library/_import to add to a <uses-library> entry + // in the AndroidManifest.xml of any Android app that includes code that references + // this module. If not set then no java_sdk_library/_import is tracked. + SdkLibraryToImplicitlyTrack *string `blueprint:"mutated"` +} + +// Structure to be embedded in a module struct that needs to support the +// SdkLibraryComponentDependency interface. +type EmbeddableSdkLibraryComponent struct { + sdkLibraryComponentProperties SdkLibraryComponentProperties +} + +func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(moduleBase *android.ModuleBase) { + moduleBase.AddProperties(&e.sdkLibraryComponentProperties) +} + +// to satisfy SdkLibraryComponentDependency +func (e *EmbeddableSdkLibraryComponent) OptionalImplicitSdkLibrary() []string { + if e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack != nil { + return []string{*e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack} + } + return nil +} + +// Implemented by modules that are (or possibly could be) a component of a java_sdk_library +// (including the java_sdk_library) itself. +type SdkLibraryComponentDependency interface { + // The optional name of the sdk library that should be implicitly added to the + // AndroidManifest of an app that contains code which references the sdk library. + // + // Returns an array containing 0 or 1 items rather than a *string to make it easier + // to append this to the list of exported sdk libraries. + OptionalImplicitSdkLibrary() []string +} + +// Make sure that all the module types that are components of java_sdk_library/_import +// and which can be referenced (directly or indirectly) from an android app implement +// the SdkLibraryComponentDependency interface. +var _ SdkLibraryComponentDependency = (*Library)(nil) +var _ SdkLibraryComponentDependency = (*Import)(nil) +var _ SdkLibraryComponentDependency = (*SdkLibrary)(nil) +var _ SdkLibraryComponentDependency = (*sdkLibraryImport)(nil) + +// Provides access to sdk_version related header and implentation jars. +type SdkLibraryDependency interface { + SdkLibraryComponentDependency + + // Get the header jars appropriate for the supplied sdk_version. + // + // These are turbine generated jars so they only change if the externals of the + // class changes but it does not contain and implementation or JavaDoc. + SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths + + // Get the implementation jars appropriate for the supplied sdk version. + // + // These are either the implementation jar for the whole sdk library or the implementation + // jars for the stubs. The latter should only be needed when generating JavaDoc as otherwise + // they are identical to the corresponding header jars. + SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths +} + type SdkLibrary struct { Library @@ -639,6 +914,8 @@ func IsXmlPermissionsFileDepTag(depTag blueprint.DependencyTag) bool { return false } +var implLibraryTag = dependencyTag{name: "impl-library"} + func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { for _, apiScope := range module.getGeneratedApiScopes(ctx) { // Add dependencies to the stubs library @@ -656,17 +933,32 @@ func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { } } - if !proptools.Bool(module.sdkLibraryProperties.Api_only) { - // Add dependency to the rule for generating the xml permissions file - ctx.AddDependency(module, xmlPermissionsFileTag, module.xmlFileName()) + if module.requiresRuntimeImplementationLibrary() { + // Add dependency to the rule for generating the implementation library. + ctx.AddDependency(module, implLibraryTag, module.implLibraryModuleName()) + + if module.sharedLibrary() { + // Add dependency to the rule for generating the xml permissions file + ctx.AddDependency(module, xmlPermissionsFileTag, module.xmlFileName()) + } + + // Only add the deps for the library if it is actually going to be built. + module.Library.deps(ctx) } +} - module.Library.deps(ctx) +func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) { + paths, err := module.commonOutputFiles(tag) + if paths == nil && err == nil { + return module.Library.OutputFiles(tag) + } else { + return paths, err + } } func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Don't build an implementation library if this is api only. - if !proptools.Bool(module.sdkLibraryProperties.Api_only) { + // Only build an implementation library if required. + if module.requiresRuntimeImplementationLibrary() { module.Library.GenerateAndroidBuildActions(ctx) } @@ -679,7 +971,7 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) // Extract information from any of the scope specific dependencies. if scopeTag, ok := tag.(scopeDependencyTag); ok { apiScope := scopeTag.apiScope - scopePaths := module.getScopePaths(apiScope) + scopePaths := module.getScopePathsCreateIfNeeded(apiScope) // Extract information from the dependency. The exact information extracted // is determined by the nature of the dependency which is determined by the tag. @@ -689,7 +981,7 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) } func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries { - if proptools.Bool(module.sdkLibraryProperties.Api_only) { + if !module.requiresRuntimeImplementationLibrary() { return nil } entriesList := module.Library.AndroidMkEntries() @@ -699,8 +991,8 @@ func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries { } // Module name of the runtime implementation library -func (module *SdkLibrary) implName() string { - return module.BaseModuleName() +func (module *SdkLibrary) implLibraryModuleName() string { + return module.BaseModuleName() + ".impl" } // Module name of the XML file for the lib @@ -744,6 +1036,27 @@ func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) stri return ":" + module.BaseModuleName() + "-removed.api." + apiScope.name + ".latest" } +// Creates the implementation java library +func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) { + props := struct { + Name *string + Visibility []string + }{ + Name: proptools.StringPtr(module.implLibraryModuleName()), + Visibility: module.sdkLibraryProperties.Impl_library_visibility, + } + + properties := []interface{}{ + &module.properties, + &module.protoProperties, + &module.deviceProperties, + &module.dexpreoptProperties, + &props, + module.sdkComponentPropertiesForChildLibrary(), + } + mctx.CreateModule(LibraryFactory, properties...) +} + // Creates a static java library that has API stubs func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { props := struct { @@ -792,7 +1105,9 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false) props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags - props.Java_version = module.properties.Java_version + // We compile the stubs for 1.8 in line with the main android.jar stubs, and potential + // interop with older developer tools that don't support 1.9. + props.Java_version = proptools.StringPtr("1.8") if module.deviceProperties.Compile_dex != nil { props.Compile_dex = module.deviceProperties.Compile_dex } @@ -805,7 +1120,7 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext props.Dist.Tag = proptools.StringPtr(".jar") } - mctx.CreateModule(LibraryFactory, &props) + mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } // Creates a droidstubs module that creates stubs source files from the given full source @@ -1010,41 +1325,43 @@ func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s sdkSpec) and return android.Paths{jarPath.Path()} } -func (module *SdkLibrary) sdkJars( - ctx android.BaseModuleContext, - sdkVersion sdkSpec, - headerJars bool) android.Paths { +// Get the apex name for module, "" if it is for platform. +func getApexNameForModule(module android.Module) string { + if apex, ok := module.(android.ApexModule); ok { + return apex.ApexName() + } - // If a specific numeric version has been requested then use prebuilt versions of the sdk. - if sdkVersion.version.isNumbered() { - return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion) - } else { - if !sdkVersion.specified() { + return "" +} + +// Check to see if the other module is within the same named APEX as this module. +// +// If either this or the other module are on the platform then this will return +// false. +func (module *SdkLibrary) withinSameApexAs(other android.Module) bool { + name := module.ApexName() + return name != "" && getApexNameForModule(other) == name +} + +func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths { + + // Only provide access to the implementation library if it is actually built. + if module.requiresRuntimeImplementationLibrary() { + // Check any special cases for java_sdk_library. + // + // Only allow access to the implementation library in the following condition: + // * No sdk_version specified on the referencing module. + // * The referencing module is in the same apex as this. + if sdkVersion.kind == sdkPrivate || module.withinSameApexAs(ctx.Module()) { if headerJars { return module.HeaderJars() } else { return module.ImplementationJars() } } - var apiScope *apiScope - switch sdkVersion.kind { - case sdkSystem: - apiScope = apiScopeSystem - case sdkTest: - apiScope = apiScopeTest - case sdkPrivate: - return module.HeaderJars() - default: - apiScope = apiScopePublic - } - - paths := module.getScopePaths(apiScope) - if headerJars { - return paths.stubsHeaderPath - } else { - return paths.stubsImplPath - } } + + return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion) } // to satisfy SdkLibraryDependency interface @@ -1148,9 +1465,20 @@ func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookCont module.createStubsLibrary(mctx, scope) } - if !proptools.Bool(module.sdkLibraryProperties.Api_only) { - // for runtime - module.createXmlFile(mctx) + if module.requiresRuntimeImplementationLibrary() { + // Create child module to create an implementation library. + // + // This temporarily creates a second implementation library that can be explicitly + // referenced. + // + // TODO(b/156618935) - update comment once only one implementation library is created. + module.createImplLibrary(mctx) + + // Only create an XML permissions file that declares the library as being usable + // as a shared library if required. + if module.sharedLibrary() { + module.createXmlFile(mctx) + } // record java_sdk_library modules so that they are exported to make javaSdkLibraries := javaSdkLibraries(mctx.Config()) @@ -1169,10 +1497,16 @@ func (module *SdkLibrary) InitSdkLibraryProperties() { &module.protoProperties, ) + module.initSdkLibraryComponent(&module.ModuleBase) + module.properties.Installable = proptools.BoolPtr(true) module.deviceProperties.IsSDKLibrary = true } +func (module *SdkLibrary) requiresRuntimeImplementationLibrary() bool { + return !proptools.Bool(module.sdkLibraryProperties.Api_only) +} + // Defines how to name the individual component modules the sdk library creates. type sdkLibraryComponentNamingScheme interface { stubsLibraryModuleName(scope *apiScope, baseName string) string @@ -1265,10 +1599,23 @@ func SdkLibraryFactory() android.Module { module.scopeToProperties = scopeToProperties // Add the properties containing visibility rules so that they are checked. + android.AddVisibilityProperty(module, "impl_library_visibility", &module.sdkLibraryProperties.Impl_library_visibility) android.AddVisibilityProperty(module, "stubs_library_visibility", &module.sdkLibraryProperties.Stubs_library_visibility) android.AddVisibilityProperty(module, "stubs_source_visibility", &module.sdkLibraryProperties.Stubs_source_visibility) module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { + // If no implementation is required then it cannot be used as a shared library + // either. + if !module.requiresRuntimeImplementationLibrary() { + // If shared_library has been explicitly set to true then it is incompatible + // with api_only: true. + if proptools.Bool(module.commonSdkLibraryProperties.Shared_library) { + ctx.PropertyErrorf("api_only/shared_library", "inconsistent settings, shared_library and api_only cannot both be true") + } + // Set shared_library: false. + module.commonSdkLibraryProperties.Shared_library = proptools.BoolPtr(false) + } + if module.initCommonAfterDefaultsApplied(ctx) { module.CreateInternalModules(ctx) } @@ -1293,10 +1640,10 @@ type sdkLibraryScopeProperties struct { Stub_srcs []string `android:"path"` // The current.txt - Current_api string `android:"path"` + Current_api *string `android:"path"` // The removed.txt - Removed_api string `android:"path"` + Removed_api *string `android:"path"` } type sdkLibraryImportProperties struct { @@ -1406,7 +1753,9 @@ func (module *sdkLibraryImport) createInternalModules(mctx android.DefaultableHo module.createJavaImportForStubs(mctx, apiScope, scopeProperties) - module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties) + if len(scopeProperties.Stub_srcs) > 0 { + module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties) + } } javaSdkLibraries := javaSdkLibraries(mctx.Config()) @@ -1433,7 +1782,8 @@ func (module *sdkLibraryImport) createJavaImportForStubs(mctx android.Defaultabl // The imports are preferred if the java_sdk_library_import is preferred. props.Prefer = proptools.BoolPtr(module.prebuilt.Prefer()) - mctx.CreateModule(ImportFactory, &props) + + mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } func (module *sdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { @@ -1458,45 +1808,48 @@ func (module *sdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) // Add dependencies to the prebuilt stubs library ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope)) + + if len(scopeProperties.Stub_srcs) > 0 { + // Add dependencies to the prebuilt stubs source library + ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, module.stubsSourceModuleName(apiScope)) + } } } +func (module *sdkLibraryImport) OutputFiles(tag string) (android.Paths, error) { + return module.commonOutputFiles(tag) +} + func (module *sdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Record the paths to the prebuilt stubs library. + // Record the paths to the prebuilt stubs library and stubs source. ctx.VisitDirectDeps(func(to android.Module) { tag := ctx.OtherModuleDependencyTag(to) - if lib, ok := to.(Dependency); ok { - if scopeTag, ok := tag.(scopeDependencyTag); ok { - apiScope := scopeTag.apiScope - scopePaths := module.getScopePaths(apiScope) - scopePaths.stubsHeaderPath = lib.HeaderJars() - } + // Extract information from any of the scope specific dependencies. + if scopeTag, ok := tag.(scopeDependencyTag); ok { + apiScope := scopeTag.apiScope + scopePaths := module.getScopePathsCreateIfNeeded(apiScope) + + // Extract information from the dependency. The exact information extracted + // is determined by the nature of the dependency which is determined by the tag. + scopeTag.extractDepInfo(ctx, to, scopePaths) } }) -} -func (module *sdkLibraryImport) sdkJars( - ctx android.BaseModuleContext, - sdkVersion sdkSpec) android.Paths { - - // If a specific numeric version has been requested then use prebuilt versions of the sdk. - if sdkVersion.version.isNumbered() { - return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion) - } + // Populate the scope paths with information from the properties. + for apiScope, scopeProperties := range module.scopeProperties { + if len(scopeProperties.Jars) == 0 { + continue + } - var apiScope *apiScope - switch sdkVersion.kind { - case sdkSystem: - apiScope = apiScopeSystem - case sdkTest: - apiScope = apiScopeTest - default: - apiScope = apiScopePublic + paths := module.getScopePathsCreateIfNeeded(apiScope) + paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api) + paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api) } +} - paths := module.getScopePaths(apiScope) - return paths.stubsHeaderPath +func (module *sdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths { + return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion) } // to satisfy SdkLibraryDependency interface @@ -1669,21 +2022,25 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe s.Scopes = make(map[*apiScope]scopeProperties) for _, apiScope := range allApiScopes { - paths := sdk.getScopePaths(apiScope) + paths := sdk.findScopePaths(apiScope) + if paths == nil { + continue + } + jars := paths.stubsImplPath if len(jars) > 0 { properties := scopeProperties{} properties.Jars = jars properties.SdkVersion = sdk.sdkVersionForStubsLibrary(ctx.SdkModuleContext(), apiScope) - properties.StubsSrcJar = paths.stubsSrcJar - properties.CurrentApiFile = paths.currentApiFilePath - properties.RemovedApiFile = paths.removedApiFilePath + properties.StubsSrcJar = paths.stubsSrcJar.Path() + properties.CurrentApiFile = paths.currentApiFilePath.Path() + properties.RemovedApiFile = paths.removedApiFilePath.Path() s.Scopes[apiScope] = properties } } s.Libs = sdk.properties.Libs - s.Naming_scheme = sdk.commonProperties.Naming_scheme + s.Naming_scheme = sdk.commonSdkLibraryProperties.Naming_scheme } func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { diff --git a/tradefed/autogen.go b/tradefed/autogen.go index 596284beb..be44cac5d 100644 --- a/tradefed/autogen.go +++ b/tradefed/autogen.go @@ -219,31 +219,39 @@ func AutoGenRustTestConfig(ctx android.ModuleContext, name string, testConfigPro } var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{ - Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template", + Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}", CommandDeps: []string{ "${AutoGenTestConfigScript}", "${EmptyTestConfig}", "$template", }, -}, "name", "template") +}, "name", "template", "extraConfigs") func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, - testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool) android.Path { + testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path { path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) + var configStrings []string if autogenPath != nil { template := "${InstrumentationTestConfigTemplate}" moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp) if moduleTemplate.Valid() { template = moduleTemplate.String() } + for _, config := range configs { + configStrings = append(configStrings, config.Config()) + } + extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent)) + extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs) + ctx.Build(pctx, android.BuildParams{ Rule: autogenInstrumentationTest, Description: "test config", Input: manifest, Output: autogenPath, Args: map[string]string{ - "name": ctx.ModuleName(), - "template": template, + "name": ctx.ModuleName(), + "template": template, + "extraConfigs": extraConfigs, }, }) return autogenPath |