// Copyright 2024 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" "android/soong/etc" "fmt" "path" "strings" "github.com/google/blueprint/proptools" ) // --------------------------------------------------------------------------------------------- // Naming scheme of the submodules generated by java_sdk_library and java_sdk_library_import // --------------------------------------------------------------------------------------------- const ( sdkXmlFileSuffix = ".xml" implLibSuffix = ".impl" ) func implLibraryModuleName(sdkLibName string) string { return sdkLibName + implLibSuffix } // Module name of the runtime implementation library func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string { return implLibraryModuleName(c.module.RootLibraryName()) } // Module name of the XML file for the lib func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string { return c.module.RootLibraryName() + sdkXmlFileSuffix } // Name of the java_library module that compiles the stubs source. func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string { baseName := c.module.RootLibraryName() return apiScope.stubsLibraryModuleName(baseName) } // Name of the java_library module that compiles the exportable stubs source. func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string { baseName := c.module.RootLibraryName() return apiScope.exportableStubsLibraryModuleName(baseName) } // Name of the droidstubs module that generates the stubs source and may also // generate/check the API. func (c *commonToSdkLibraryAndImport) droidstubsModuleName(apiScope *apiScope) string { baseName := c.module.RootLibraryName() return apiScope.stubsSourceModuleName(baseName) } // Name of the java_api_library module that generates the from-text stubs source // and compiles to a jar file. func (c *commonToSdkLibraryAndImport) fromTextStubsLibraryModuleName(apiScope *apiScope) string { baseName := c.module.RootLibraryName() return apiScope.apiLibraryModuleName(baseName) } // Name of the java_library module that compiles the stubs // generated from source Java files. func (c *commonToSdkLibraryAndImport) fromSourceStubsLibraryModuleName(apiScope *apiScope) string { baseName := c.module.RootLibraryName() return apiScope.sourceStubsLibraryModuleName(baseName) } // Name of the java_library module that compiles the exportable stubs // generated from source Java files. func (c *commonToSdkLibraryAndImport) exportableFromSourceStubsLibraryModuleName(apiScope *apiScope) string { baseName := c.module.RootLibraryName() return apiScope.exportableSourceStubsLibraryModuleName(baseName) } // --------------------------------------------------------------------------------------------- // Build rules of the submodules generated by java_sdk_library. // java_sdk_library "framework-foo" generates the following submodules: // // - "framework-foo.impl" (type: [Library]): the implementation library, which generates the // compilation outputs that include the implementation details and the private apis // (i.e. class/methods that are annotated @hide). // // - "framework-foo.stubs.source.<[apiScope.name]>" (type: [Droidstubs]): droidstubs module that // generates the stubs and the api files for the given api scope. // // - "framework-foo.stubs.<[apiScope.name]>" (type: [Library]): stub library module that // provides the compilation output of the stubs to the reverse dependencies. The module // itself does not perform any compilation actions; the module statically depends on one of // the from-source stub module or the from-text stub configuration based on the build // configuration. // // - "framework-foo.stubs.<[apiScope.name]>.from-source" (type: [Library]): stub library module // that compiles the stubs generated by the droidstubs submodule. This module is a static // dependency of the stub library module when // [android/soong/android/config.BuildFromTextStub()] is false. // // - "framework-foo.stubs.<[apiScope.name]>.from-text" (type: [ApiLibrary]): api library module // that generates and compiles the stubs from the api files checked in the tree instead of // the source Java files (e.g. *-current.txt files). This module is a static dependency of // the stub library module when [android/soong/android/config.BuildFromTextStub()] is true. // // - "framework-foo.stubs.exportable.<[apiScope.name]>" (type: [Library]): stub library module // that provides the "exportable" stubs. "exportable" stubs are the stubs that do not // include in-development flagged apis. This module is only used for SDK builds to generate // the SDK artifacts, and not purposed for consumption for other modules. // // - "framework-foo.stubs.exportable.<[apiScope.name]>.from-source" (type: [Library]): stub // library module that compiles the "exportable" stubs generated by the droidstubs // submodule. This module is always a static dependency of the "exportable" stub library // module given that from-text stubs cannot be used for SDK builds as it does not contain // documentations. // // - "framework-foo.xml" (type: [sdkLibraryXml]): xml library that generates the permission xml // file, which allows [SdkLibrary] to be used with tag in the // AndroidManifest.xml files. // --------------------------------------------------------------------------------------------- // Creates the implementation [Library] with ".impl" suffix. func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) { visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility) staticLibs := module.properties.Static_libs.Clone() staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs) props := struct { Name *string Enabled proptools.Configurable[bool] Visibility []string Libs []string Static_libs proptools.Configurable[[]string] Apex_available []string Stem *string }{ Name: proptools.StringPtr(module.implLibraryModuleName()), Enabled: module.EnabledProperty(), Visibility: visibility, Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...), Static_libs: staticLibs, // Pass the apex_available settings down so that the impl library can be statically // embedded within a library that is added to an APEX. Needed for updatable-media. Apex_available: module.ApexAvailable(), Stem: proptools.StringPtr(module.Name()), } properties := []interface{}{ &module.properties, &module.protoProperties, &module.deviceProperties, &module.dexProperties, &module.dexpreoptProperties, &module.linter.properties, &module.overridableProperties, &props, module.sdkComponentPropertiesForChildLibrary(), } mctx.CreateModule(LibraryFactory, properties...) } // Creates the [Droidstubs] module with ".stubs.source.<[apiScope.name]>" that creates stubs // source files from the given full source files and also updates and checks the API // specification files (i.e. "*-current.txt", "*-removed.txt" files). func (module *SdkLibrary) createDroidstubs(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) { props := struct { Name *string Enabled proptools.Configurable[bool] Visibility []string Srcs []string Installable *bool Sdk_version *string Api_surface *string System_modules *string Libs proptools.Configurable[[]string] Output_javadoc_comments *bool Arg_files []string Args *string Java_version *string Annotations_enabled *bool Merge_annotations_dirs []string Merge_inclusion_annotations_dirs []string Generate_stubs *bool Previous_api *string Aconfig_declarations []string Check_api struct { Current ApiToCheck Last_released ApiToCheck Api_lint struct { Enabled *bool New_since *string Baseline_file *string } } Aidl struct { Include_dirs []string Local_include_dirs []string } Dists []android.Dist }{} // The stubs source processing uses the same compile time classpath when extracting the // API from the implementation library as it does when compiling it. i.e. the same // * sdk version // * system_modules // * libs (static_libs/libs) props.Name = proptools.StringPtr(name) props.Enabled = module.EnabledProperty() props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility) props.Srcs = append(props.Srcs, module.properties.Srcs...) props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...) props.Sdk_version = module.deviceProperties.Sdk_version props.Api_surface = &apiScope.name props.System_modules = module.deviceProperties.System_modules props.Installable = proptools.BoolPtr(false) // A droiddoc module has only one Libs property and doesn't distinguish between // shared libs and static libs. So we need to add both of these libs to Libs property. props.Libs = proptools.NewConfigurable[[]string](nil, nil) props.Libs.AppendSimpleValue(module.properties.Libs) props.Libs.Append(module.properties.Static_libs) props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs) props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs) props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs props.Java_version = module.properties.Java_version props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations droidstubsArgs := []string{} if len(module.sdkLibraryProperties.Api_packages) != 0 { droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":")) } droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...) disabledWarnings := []string{"HiddenSuperclass"} if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) { disabledWarnings = append(disabledWarnings, "BroadcastBehavior", "DeprecationMismatch", "MissingPermission", "SdkConstant", "Todo", ) } droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide ")) // Output Javadoc comments for public scope. if apiScope == apiScopePublic { props.Output_javadoc_comments = proptools.BoolPtr(true) } // Add in scope specific arguments. droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...) props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " ")) // List of APIs identified from the provided source files are created. They are later // compared against to the not-yet-released (a.k.a current) list of APIs and to the // last-released (a.k.a numbered) list of API. currentApiFileName := apiScope.apiFilePrefix + "current.txt" removedApiFileName := apiScope.apiFilePrefix + "removed.txt" apiDir := module.getApiDir() currentApiFileName = path.Join(apiDir, currentApiFileName) removedApiFileName = path.Join(apiDir, removedApiFileName) // check against the not-yet-release API props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName) props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName) if module.compareAgainstLatestApi(apiScope) { // check against the latest released API latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope)) props.Previous_api = latestApiFilegroupName props.Check_api.Last_released.Api_file = latestApiFilegroupName props.Check_api.Last_released.Removed_api_file = proptools.StringPtr( module.latestRemovedApiFilegroupName(apiScope)) props.Check_api.Last_released.Baseline_file = proptools.StringPtr( module.latestIncompatibilitiesFilegroupName(apiScope)) if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) { // Enable api lint. props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true) props.Check_api.Api_lint.New_since = latestApiFilegroupName // If it exists then pass a lint-baseline.txt through to droidstubs. baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt") baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath) paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil) if err != nil { mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err) } if len(paths) == 1 { props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath) } else if len(paths) != 0 { mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths) } } } if !Bool(module.sdkLibraryProperties.No_dist) { // Dist the api txt and removed api txt artifacts for sdk builds. distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api")) stubsTypeTagPrefix := "" if mctx.Config().ReleaseHiddenApiExportableStubs() { stubsTypeTagPrefix = ".exportable" } for _, p := range []struct { tag string pattern string }{ // "exportable" api files are copied to the dist directory instead of the // "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag // is set. Otherwise, the "everything" api files are copied to the dist directory. {tag: "%s.api.txt", pattern: "%s.txt"}, {tag: "%s.removed-api.txt", pattern: "%s-removed.txt"}, } { props.Dists = append(props.Dists, android.Dist{ Targets: []string{"sdk", "win_sdk"}, Dir: distDir, Dest: proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())), Tag: proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)), }) } } mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx) } type libraryProperties struct { Name *string Enabled proptools.Configurable[bool] Visibility []string Srcs []string Installable *bool Sdk_version *string System_modules *string Patch_module *string Libs []string Static_libs []string Compile_dex *bool Java_version *string Openjdk9 struct { Srcs []string Javacflags []string } Dist struct { Targets []string Dest *string Dir *string Tag *string } Is_stubs_module *bool Stub_contributing_api *string } func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties { props := libraryProperties{} props.Enabled = module.EnabledProperty() props.Visibility = []string{"//visibility:override", "//visibility:private"} // sources are generated from the droiddoc sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope) props.Sdk_version = proptools.StringPtr(sdkVersion) props.System_modules = module.deviceProperties.System_modules props.Patch_module = module.properties.Patch_module props.Installable = proptools.BoolPtr(false) props.Libs = module.sdkLibraryProperties.Stub_only_libs props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...) props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs // The stub-annotations library contains special versions of the annotations // with CLASS retention policy, so that they're kept. if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) { props.Libs = append(props.Libs, "stub-annotations") } props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags // 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") props.Is_stubs_module = proptools.BoolPtr(true) props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String()) return props } // Creates the from-source stub [Library] with ".stubs.<[apiScope.name]>.from-source" suffix. func (module *SdkLibrary) createFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { props := module.stubsLibraryProps(mctx, apiScope) props.Name = proptools.StringPtr(module.fromSourceStubsLibraryModuleName(apiScope)) props.Srcs = []string{":" + module.droidstubsModuleName(apiScope)} mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } // Creates the "exportable" from-source stub [Library] with // ".stubs.exportable.<[apiScope.name]>" suffix. func (module *SdkLibrary) createExportableFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { props := module.stubsLibraryProps(mctx, apiScope) props.Name = proptools.StringPtr(module.exportableFromSourceStubsLibraryModuleName(apiScope)) props.Srcs = []string{":" + module.droidstubsModuleName(apiScope) + "{.exportable}"} mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } // Creates the from-text stub [ApiLibrary] with ".stubs.<[apiScope.name]>.from-text" suffix. func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { props := struct { Name *string Enabled proptools.Configurable[bool] Visibility []string Api_contributions []string Libs proptools.Configurable[[]string] Static_libs []string System_modules *string Enable_validation *bool Stubs_type *string Sdk_version *string Previous_api *string }{} props.Name = proptools.StringPtr(module.fromTextStubsLibraryModuleName(apiScope)) props.Enabled = module.EnabledProperty() props.Visibility = []string{"//visibility:override", "//visibility:private"} apiContributions := []string{} // Api surfaces are not independent of each other, but have subset relationships, // and so does the api files. To generate from-text stubs for api surfaces other than public, // all subset api domains' api_contriubtions must be added as well. scope := apiScope for scope != nil { apiContributions = append(apiContributions, module.droidstubsModuleName(scope)+".api.contribution") scope = scope.extends } if apiScope == apiScopePublic { additionalApiContribution := module.apiLibraryAdditionalApiContribution() if additionalApiContribution != "" { apiContributions = append(apiContributions, additionalApiContribution) } } props.Api_contributions = apiContributions // Ensure that stub-annotations is added to the classpath before any other libs props.Libs = proptools.NewConfigurable[[]string](nil, nil) props.Libs.AppendSimpleValue([]string{"stub-annotations"}) props.Libs.AppendSimpleValue(module.properties.Libs) props.Libs.Append(module.properties.Static_libs) props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs) props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs) props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs props.System_modules = module.deviceProperties.System_modules props.Enable_validation = proptools.BoolPtr(true) props.Stubs_type = proptools.StringPtr("everything") if module.deviceProperties.Sdk_version != nil { props.Sdk_version = module.deviceProperties.Sdk_version } if module.compareAgainstLatestApi(apiScope) { // check against the latest released API latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope)) props.Previous_api = latestApiFilegroupName } mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties { props := libraryProperties{} props.Enabled = module.EnabledProperty() props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility) sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope) props.Sdk_version = proptools.StringPtr(sdkVersion) props.System_modules = module.deviceProperties.System_modules // The imports need to be compiled to dex if the java_sdk_library requests it. compileDex := module.dexProperties.Compile_dex if module.stubLibrariesCompiledForDex() { compileDex = proptools.BoolPtr(true) } props.Compile_dex = compileDex props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String()) if !Bool(module.sdkLibraryProperties.No_dist) && doDist { props.Dist.Targets = []string{"sdk", "win_sdk"} props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem())) props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope)) props.Dist.Tag = proptools.StringPtr(".jar") } props.Is_stubs_module = proptools.BoolPtr(true) return props } // Creates the stub [Library] with ".stubs.<[apiScope.name]>" suffix. func (module *SdkLibrary) createTopLevelStubsLibrary( mctx android.DefaultableHookContext, apiScope *apiScope) { // Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false doDist := !mctx.Config().ReleaseHiddenApiExportableStubs() props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) // Add the stub compiling java_library/java_api_library as static lib based on build config staticLib := module.fromSourceStubsLibraryModuleName(apiScope) if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() { staticLib = module.fromTextStubsLibraryModuleName(apiScope) } props.Static_libs = append(props.Static_libs, staticLib) mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } // Creates the "exportable" stub [Library] with ".stubs.exportable.<[apiScope.name]>" suffix. func (module *SdkLibrary) createTopLevelExportableStubsLibrary( mctx android.DefaultableHookContext, apiScope *apiScope) { // Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true doDist := mctx.Config().ReleaseHiddenApiExportableStubs() props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope)) staticLib := module.exportableFromSourceStubsLibraryModuleName(apiScope) props.Static_libs = append(props.Static_libs, staticLib) mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } // Creates the [sdkLibraryXml] with ".xml" suffix. func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { moduleMinApiLevel := module.Library.MinSdkVersion(mctx) var moduleMinApiLevelStr = moduleMinApiLevel.String() if moduleMinApiLevel == android.NoneApiLevel { moduleMinApiLevelStr = "current" } props := struct { Name *string Enabled proptools.Configurable[bool] Lib_name *string Apex_available []string On_bootclasspath_since *string On_bootclasspath_before *string Min_device_sdk *string Max_device_sdk *string Sdk_library_min_api_level *string Uses_libs_dependencies proptools.Configurable[[]string] }{ Name: proptools.StringPtr(module.xmlPermissionsModuleName()), Enabled: module.EnabledProperty(), Lib_name: proptools.StringPtr(module.BaseModuleName()), Apex_available: module.ApexProperties.Apex_available, On_bootclasspath_since: module.commonSdkLibraryProperties.On_bootclasspath_since, On_bootclasspath_before: module.commonSdkLibraryProperties.On_bootclasspath_before, Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk, Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk, Sdk_library_min_api_level: &moduleMinApiLevelStr, Uses_libs_dependencies: module.usesLibraryProperties.Uses_libs.Clone(), } mctx.CreateModule(sdkLibraryXmlFactory, &props) } // --------------------------------------------------------------------------------------------- // Build rules of the submodules generated by java_sdk_library_import. // Note that the java_sdk_library_import module does not generate the implementation library. // Instead, it will create a dependency to the source implemenetation library if one exists. // java_sdk_library_import "framework-foo" generates the following submodules: // // - "framework-foo.stubs.<[apiScope.name]>" (type: [Import]): prebuilt stub library module that // provides the stub jar file checked in the tree. // // - "framework-foo.stubs.source.<[apiScope.name]>" (type: [PrebuiltStubsSources]): prebuilt // droidstubs module that provides the stub source jar file checked in the tree. // // - "framework-foo.stubs.source.<[apiScope.name]>.api.contribution" // (type [JavaApiContributionImport]): prebuilt java_api_contribution module that provides // the prebuilt api file for previously released from-text stub generation. // --------------------------------------------------------------------------------------------- // Creates the prebuilt stub [Import] with ".stubs.<[apiScope.name]>" suffix. func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { // Creates a java import for the jar with ".stubs" suffix props := struct { Name *string Source_module_name *string Created_by_java_sdk_library_name *string Sdk_version *string Libs []string Jars []string Compile_dex *bool Is_stubs_module *bool android.UserSuppliedPrebuiltProperties }{} props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName())) props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) props.Sdk_version = scopeProperties.Sdk_version // Prepend any of the libs from the legacy public properties to the libs for each of the // scopes to avoid having to duplicate them in each scope. props.Libs = append(module.properties.Libs, scopeProperties.Libs...) props.Jars = scopeProperties.Jars // The imports are preferred if the java_sdk_library_import is preferred. props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt) // The imports need to be compiled to dex if the java_sdk_library_import requests it. compileDex := module.properties.Compile_dex if module.stubLibrariesCompiledForDex() { compileDex = proptools.BoolPtr(true) } props.Compile_dex = compileDex props.Is_stubs_module = proptools.BoolPtr(true) mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { props := struct { Name *string Source_module_name *string Created_by_java_sdk_library_name *string Srcs []string android.UserSuppliedPrebuiltProperties }{} props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope)) props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName())) props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) props.Srcs = scopeProperties.Stub_srcs // The stubs source is preferred if the java_sdk_library_import is preferred. props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt) mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } // Creates the prebuilt api contribution [JavaApiContributionImport] with // ".stubs.source.<[apiScope.name]>.api.contribution" suffix. func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { api_file := scopeProperties.Current_api api_surface := &apiScope.name props := struct { Name *string Source_module_name *string Created_by_java_sdk_library_name *string Api_surface *string Api_file *string Visibility []string }{} props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope) + ".api.contribution") props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution") props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) props.Api_surface = api_surface props.Api_file = api_file props.Visibility = []string{"//visibility:override", "//visibility:public"} mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } // --------------------------------------------------------------------------------------------- // End of the build rules of the submodules generated by java_sdk_library_import. // --------------------------------------------------------------------------------------------- // Definition of the [sdkLibraryXml] module. The module generates the permissions xml file, // so that the apps can specify the java_sdk_library using tag in the // AndroidManifest.xml file. type sdkLibraryXml struct { android.ModuleBase android.DefaultableModuleBase android.ApexModuleBase properties sdkLibraryXmlProperties outputFilePath android.OutputPath installDirPath android.InstallPath hideApexVariantFromMake bool } type sdkLibraryXmlProperties struct { // canonical name of the lib Lib_name *string // Signals that this shared library is part of the bootclasspath starting // on the version indicated in this attribute. // // This will make platforms at this level and above to ignore // tags with this library name because the library is already // available On_bootclasspath_since *string // Signals that this shared library was part of the bootclasspath before // (but not including) the version indicated in this attribute. // // The system will automatically add a tag with this library to // apps that target any SDK less than the version indicated in this attribute. On_bootclasspath_before *string // Indicates that PackageManager should ignore this shared library if the // platform is below the version indicated in this attribute. // // This means that the device won't recognise this library as installed. Min_device_sdk *string // Indicates that PackageManager should ignore this shared library if the // platform is above the version indicated in this attribute. // // This means that the device won't recognise this library as installed. Max_device_sdk *string // The SdkLibrary's min api level as a string // // This value comes from the ApiLevel of the MinSdkVersion property. Sdk_library_min_api_level *string // Uses-libs dependencies that the shared library requires to work correctly. // // This will add dependency="foo:bar" to the section. Uses_libs_dependencies proptools.Configurable[[]string] } // java_sdk_library_xml builds the permission xml file for a java_sdk_library. // Not to be used directly by users. java_sdk_library internally uses this. func sdkLibraryXmlFactory() android.Module { module := &sdkLibraryXml{} module.AddProperties(&module.properties) android.InitApexModule(module) android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) return module } func (module *sdkLibraryXml) UniqueApexVariations() bool { // sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the // mounted APEX, which contains the name of the APEX. return true } // from android.PrebuiltEtcModule func (module *sdkLibraryXml) BaseDir() string { return "etc" } // from android.PrebuiltEtcModule func (module *sdkLibraryXml) SubDir() string { return "permissions" } var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil) // from android.ApexModule func (module *sdkLibraryXml) AvailableFor(what string) bool { return true } func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) { // do nothing } var _ android.ApexModule = (*sdkLibraryXml)(nil) // Implements android.ApexModule func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { // sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked return nil } // File path to the runtime implementation library func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string { implName := proptools.String(module.properties.Lib_name) if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() { // TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name. // In most cases, this works fine. But when apex_name is set or override_apex is used // this can be wrong. return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName) } partition := "system" if module.SocSpecific() { partition = "vendor" } else if module.DeviceSpecific() { partition = "odm" } else if module.ProductSpecific() { partition = "product" } else if module.SystemExtSpecific() { partition = "system_ext" } return "/" + partition + "/framework/" + implName + ".jar" } func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string { if value == nil { return "" } apiLevel, err := android.ApiLevelFromUser(ctx, *value) if err != nil { // attributes in bp files have underscores but in the xml have dashes. ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error()) return "" } if apiLevel.IsCurrent() { // passing "current" would always mean a future release, never the current (or the current in // progress) which means some conditions would never be triggered. ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), `"current" is not an allowed value for this attribute`) return "" } // "safeValue" is safe because it translates finalized codenames to a string // with their SDK int. safeValue := apiLevel.String() return formattedOptionalAttribute(attrName, &safeValue) } // formats an attribute for the xml permissions file if the value is not null // returns empty string otherwise func formattedOptionalAttribute(attrName string, value *string) string { if value == nil { return "" } return fmt.Sprintf(" %s=\"%s\"\n", attrName, *value) } func formattedDependenciesAttribute(dependencies []string) string { if dependencies == nil { return "" } return fmt.Sprintf(" dependency=\"%s\"\n", strings.Join(dependencies, ":")) } func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string { libName := proptools.String(module.properties.Lib_name) libNameAttr := formattedOptionalAttribute("name", &libName) filePath := module.implPath(ctx) filePathAttr := formattedOptionalAttribute("file", &filePath) implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since) implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before) minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk) maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk) dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies.GetOrDefault(ctx, nil)) // is understood in all android versions whereas is only understood from API T (and ignored before that). // similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T var libraryTag string if module.properties.Min_device_sdk != nil { libraryTag = " \n", "\n", "\n", libraryTag, libNameAttr, filePathAttr, implicitFromAttr, implicitUntilAttr, minSdkAttr, maxSdkAttr, dependenciesAttr, " />\n", "\n", }, "") } func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) { apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) module.hideApexVariantFromMake = !apexInfo.IsForPlatform() libName := proptools.String(module.properties.Lib_name) module.selfValidate(ctx) xmlContent := module.permissionsContents(ctx) module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent) module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir()) ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath) ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "") } func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { if module.hideApexVariantFromMake { return []android.AndroidMkEntries{{ Disabled: true, }} } return []android.AndroidMkEntries{{ Class: "ETC", OutputFile: android.OptionalPathForPath(module.outputFilePath), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetString("LOCAL_MODULE_TAGS", "optional") entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base()) }, }, }} } func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) { module.validateAtLeastTAttributes(ctx) module.validateMinAndMaxDeviceSdk(ctx) module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx) module.validateOnBootclasspathBeforeRequirements(ctx) } func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) { t := android.ApiLevelOrPanic(ctx, "Tiramisu") module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk") module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk") module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before") module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since") } func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) { if attr != nil { if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil { // we will inform the user of invalid inputs when we try to write the // permissions xml file so we don't need to do it here if t.GreaterThan(level) { ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T") } } } } func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) { if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil { min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) if minErr == nil && maxErr == nil { // we will inform the user of invalid inputs when we try to write the // permissions xml file so we don't need to do it here if min.GreaterThan(max) { ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk") } } } } func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) { moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) if module.properties.Min_device_sdk != nil { api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) if err == nil { if moduleMinApi.GreaterThan(api) { ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) } } } if module.properties.Max_device_sdk != nil { api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) if err == nil { if moduleMinApi.GreaterThan(api) { ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) } } } } func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) { moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) if module.properties.On_bootclasspath_before != nil { t := android.ApiLevelOrPanic(ctx, "Tiramisu") // if we use the attribute, then we need to do this validation if moduleMinApi.LessThan(t) { // if minAPi is < T, then we need to have min_device_sdk (which only accepts T+) if module.properties.Min_device_sdk == nil { ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T") } } } }