diff --git a/android/module.go b/android/module.go index bfb87fa75..337ae4076 100644 --- a/android/module.go +++ b/android/module.go @@ -259,6 +259,8 @@ type Module interface { SkipInstall() IsSkipInstall() bool MakeUninstallable() + ReplacedByPrebuilt() + IsReplacedByPrebuilt() bool ExportedToMake() bool InitRc() Paths VintfFragments() Paths @@ -543,6 +545,9 @@ type commonProperties struct { SkipInstall bool `blueprint:"mutated"` + // Whether the module has been replaced by a prebuilt + ReplacedByPrebuilt bool `blueprint:"mutated"` + // Disabled by mutators. If set to true, it overrides Enabled property. ForcedDisabled bool `blueprint:"mutated"` @@ -1068,6 +1073,15 @@ func (m *ModuleBase) MakeUninstallable() { m.SkipInstall() } +func (m *ModuleBase) ReplacedByPrebuilt() { + m.commonProperties.ReplacedByPrebuilt = true + m.SkipInstall() +} + +func (m *ModuleBase) IsReplacedByPrebuilt() bool { + return m.commonProperties.ReplacedByPrebuilt +} + func (m *ModuleBase) ExportedToMake() bool { return m.commonProperties.NamespaceExportedToMake } diff --git a/android/prebuilt.go b/android/prebuilt.go index 269ad5d8a..734871b6b 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -253,7 +253,7 @@ func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) { p := m.(PrebuiltInterface).Prebuilt() if p.usePrebuilt(ctx, s) { p.properties.UsePrebuilt = true - s.SkipInstall() + s.ReplacedByPrebuilt() } }) } diff --git a/java/Android.bp b/java/Android.bp index e345014ce..92e8ca458 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -59,6 +59,7 @@ bootstrap_go_package { "device_host_converter_test.go", "dexpreopt_test.go", "dexpreopt_bootjars_test.go", + "hiddenapi_singleton_test.go", "java_test.go", "jdeps_test.go", "kotlin_test.go", diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index 29b6bcd7d..b6af3bf28 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -163,6 +163,7 @@ func stubFlagsRule(ctx android.SingletonContext) { return } } + bootDexJars = append(bootDexJars, jar) } } diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go new file mode 100644 index 000000000..bcca93a00 --- /dev/null +++ b/java/hiddenapi_singleton_test.go @@ -0,0 +1,136 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "android/soong/android" + "strings" + "testing" +) + +func testConfigWithBootJars(bp string, bootJars []string) android.Config { + config := testConfig(nil, bp, nil) + config.TestProductVariables.BootJars = bootJars + return config +} + +func testContextWithHiddenAPI() *android.TestContext { + ctx := testContext() + ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory) + return ctx +} + +func testHiddenAPI(t *testing.T, bp string, bootJars []string) (*android.TestContext, android.Config) { + t.Helper() + + config := testConfigWithBootJars(bp, bootJars) + ctx := testContextWithHiddenAPI() + + run(t, ctx, config) + + return ctx, config +} + +func TestHiddenAPISingleton(t *testing.T) { + ctx, _ := testHiddenAPI(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + compile_dex: true, + } + `, []string{":foo"}) + + hiddenAPI := ctx.SingletonForTests("hiddenapi") + hiddenapiRule := hiddenAPI.Rule("hiddenapi") + want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar" + if !strings.Contains(hiddenapiRule.RuleParams.Command, want) { + t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command) + } +} + +func TestHiddenAPISingletonWithPrebuilt(t *testing.T) { + ctx, _ := testHiddenAPI(t, ` + java_import { + name: "foo", + jars: ["a.jar"], + compile_dex: true, + } + `, []string{":foo"}) + + hiddenAPI := ctx.SingletonForTests("hiddenapi") + hiddenapiRule := hiddenAPI.Rule("hiddenapi") + want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar" + if !strings.Contains(hiddenapiRule.RuleParams.Command, want) { + t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command) + } +} + +func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) { + ctx, _ := testHiddenAPI(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + compile_dex: true, + } + + java_import { + name: "foo", + jars: ["a.jar"], + compile_dex: true, + prefer: false, + } + `, []string{":foo"}) + + hiddenAPI := ctx.SingletonForTests("hiddenapi") + hiddenapiRule := hiddenAPI.Rule("hiddenapi") + fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar" + if !strings.Contains(hiddenapiRule.RuleParams.Command, fromSourceJarArg) { + t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command) + } + + prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar" + if strings.Contains(hiddenapiRule.RuleParams.Command, prebuiltJarArg) { + t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", prebuiltJarArg, hiddenapiRule.RuleParams.Command) + } +} + +func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) { + ctx, _ := testHiddenAPI(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + compile_dex: true, + } + + java_import { + name: "foo", + jars: ["a.jar"], + compile_dex: true, + prefer: true, + } + `, []string{":foo"}) + + hiddenAPI := ctx.SingletonForTests("hiddenapi") + hiddenapiRule := hiddenAPI.Rule("hiddenapi") + prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/prebuilt_foo/android_common/dex/foo.jar" + if !strings.Contains(hiddenapiRule.RuleParams.Command, prebuiltJarArg) { + t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", prebuiltJarArg, hiddenapiRule.RuleParams.Command) + } + + fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar" + if strings.Contains(hiddenapiRule.RuleParams.Command, fromSourceJarArg) { + t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command) + } +} diff --git a/java/java.go b/java/java.go index 82518c9db..d67e9e098 100644 --- a/java/java.go +++ b/java/java.go @@ -1614,6 +1614,9 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { configurationName := j.ConfigurationName() primary := configurationName == ctx.ModuleName() + // If the prebuilt is being used rather than the from source, skip this + // module to prevent duplicated classes + primary = primary && !j.IsReplacedByPrebuilt() // Hidden API CSV generation and dex encoding dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, j.implementationJarFile, @@ -2684,6 +2687,13 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } + configurationName := j.BaseModuleName() + primary := j.Prebuilt().UsePrebuilt() + + // Hidden API CSV generation and dex encoding + dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile, + proptools.Bool(j.dexProperties.Uncompress_dex)) + j.dexJarFile = dexOutputFile } } diff --git a/java/testing.go b/java/testing.go index 70c857f39..322dc9ec6 100644 --- a/java/testing.go +++ b/java/testing.go @@ -145,6 +145,7 @@ func GatherRequiredDepsForTest() string { srcs: ["a.java"], sdk_version: "none", system_modules: "stable-core-platform-api-stubs-system-modules", + compile_dex: true, } `, extra) }