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/apex/apex.go b/apex/apex.go index dbbfe78af..05ba5c4cb 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -785,6 +785,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 +1829,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 +1883,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 +2210,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 3979149c2..a2d7a9713 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -4437,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() @@ -4449,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, } @@ -4519,6 +4527,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"], @@ -4540,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 { @@ -4580,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) { @@ -4587,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) { @@ -4602,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/app.go b/java/app.go index 1fc18fbbf..e585dc850 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()) } @@ -880,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 { 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 {