1// Copyright (C) 2021 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package aidl
16
17import (
18	"android/soong/android"
19	"android/soong/cc"
20	"android/soong/java"
21	"android/soong/rust"
22
23	"path/filepath"
24	"strings"
25
26	"github.com/google/blueprint/proptools"
27)
28
29func addLibrary(mctx android.LoadHookContext, i *aidlInterface, version string, lang string) string {
30	if lang == langJava {
31		return addJavaLibrary(mctx, i, version)
32	} else if lang == langRust {
33		return addRustLibrary(mctx, i, version)
34	}
35	return addCppLibrary(mctx, i, version, lang)
36}
37
38func addCppLibrary(mctx android.LoadHookContext, i *aidlInterface, version string, lang string) string {
39	cppSourceGen := i.versionedName(version) + "-" + lang + "-source"
40	cppModuleGen := i.versionedName(version) + "-" + lang
41
42	srcs, aidlRoot := i.srcsForVersion(mctx, version)
43	if len(srcs) == 0 {
44		// This can happen when the version is about to be frozen; the version
45		// directory is created but API dump hasn't been copied there.
46		// Don't create a library for the yet-to-be-frozen version.
47		return ""
48	}
49
50	var overrideVndkProperties cc.VndkProperties
51
52	if !i.isModuleForVndk(version) {
53		// We only want the VNDK to include the latest interface. For interfaces in
54		// development, they will be frozen, so we put their latest version in the
55		// VNDK. For interfaces which are already frozen, we put their latest version
56		// in the VNDK, and when that version is frozen, the version in the VNDK can
57		// be updated. Otherwise, we remove this library from the VNDK, to avoid adding
58		// multiple versions of the same library to the VNDK.
59		overrideVndkProperties.Vndk.Enabled = proptools.BoolPtr(false)
60		overrideVndkProperties.Vndk.Support_system_process = proptools.BoolPtr(false)
61	}
62
63	var commonProperties *CommonNativeBackendProperties
64	if lang == langCpp {
65		commonProperties = &i.properties.Backend.Cpp.CommonNativeBackendProperties
66	} else if lang == langNdk || lang == langNdkPlatform {
67		commonProperties = &i.properties.Backend.Ndk.CommonNativeBackendProperties
68	}
69
70	genLog := proptools.Bool(commonProperties.Gen_log)
71	genTrace := proptools.Bool(i.properties.Gen_trace)
72
73	mctx.CreateModule(aidlGenFactory, &nameProperties{
74		Name: proptools.StringPtr(cppSourceGen),
75	}, &aidlGenProperties{
76		Srcs:                  srcs,
77		AidlRoot:              aidlRoot,
78		IsToT:                 version == i.nextVersion(),
79		ImportsWithoutVersion: i.properties.ImportsWithoutVersion,
80		Stability:             i.properties.Stability,
81		Lang:                  lang,
82		BaseName:              i.ModuleBase.Name(),
83		GenLog:                genLog,
84		Version:               i.versionForAidlGenRule(version),
85		GenTrace:              genTrace,
86		Unstable:              i.properties.Unstable,
87		Visibility:            srcsVisibility(mctx, lang),
88		Flags:                 i.flagsForAidlGenRule(version),
89	})
90
91	importExportDependencies := []string{}
92	var sharedLibDependency []string
93	var headerLibs []string
94	var sdkVersion *string
95	var minSdkVersion *string
96	var stl *string
97	var cpp_std *string
98	var hostSupported *bool
99	var addCflags []string
100
101	if lang == langCpp {
102		importExportDependencies = append(importExportDependencies, "libbinder", "libutils")
103		if genTrace {
104			sharedLibDependency = append(sharedLibDependency, "libcutils")
105		}
106		hostSupported = i.properties.Host_supported
107		minSdkVersion = i.properties.Backend.Cpp.Min_sdk_version
108	} else if lang == langNdk {
109		importExportDependencies = append(importExportDependencies, "libbinder_ndk")
110		if genTrace {
111			sharedLibDependency = append(sharedLibDependency, "libandroid")
112		}
113		sdkVersion = proptools.StringPtr("current")
114		stl = proptools.StringPtr("c++_shared")
115		minSdkVersion = i.properties.Backend.Ndk.Min_sdk_version
116	} else if lang == langNdkPlatform {
117		importExportDependencies = append(importExportDependencies, "libbinder_ndk")
118		if genTrace {
119			headerLibs = append(headerLibs, "libandroid_aidltrace")
120			sharedLibDependency = append(sharedLibDependency, "libcutils")
121		}
122		hostSupported = i.properties.Host_supported
123		addCflags = append(addCflags, "-DBINDER_STABILITY_SUPPORT")
124		minSdkVersion = i.properties.Backend.Ndk.Min_sdk_version
125	} else {
126		panic("Unrecognized language: " + lang)
127	}
128
129	vendorAvailable := i.properties.Vendor_available
130	odmAvailable := i.properties.Odm_available
131	productAvailable := i.properties.Product_available
132	if lang == langCpp {
133		// Vendor and product modules cannot use the libbinder (cpp) backend of AIDL in a
134		// way that is stable. So, in order to prevent accidental usage of these library by
135		// vendor and product forcibly disabling this version of the library.
136		//
137		// It may be the case in the future that we will want to enable this (if some generic
138		// helper should be used by both libbinder vendor things using /dev/vndbinder as well
139		// as those things using /dev/binder + libbinder_ndk to talk to stable interfaces).
140		if "vintf" == proptools.String(i.properties.Stability) {
141			overrideVndkProperties.Vndk.Private = proptools.BoolPtr(true)
142		}
143		// As libbinder is not available for the product processes, we must not create
144		// product variant for the aidl_interface
145		productAvailable = nil
146	}
147
148	if lang == langNdk {
149		// TODO(b/121157555): when the NDK variant is its own variant, these wouldn't interact,
150		// but we can't create a vendor or product version of an NDK variant
151		//
152		// nil (unspecified) is used instead of false so that this can't conflict with
153		// 'vendor: true', for instance.
154		vendorAvailable = nil
155		odmAvailable = nil
156		productAvailable = nil
157		overrideVndkProperties.Vndk.Enabled = proptools.BoolPtr(false)
158		overrideVndkProperties.Vndk.Support_system_process = proptools.BoolPtr(false)
159	}
160
161	mctx.CreateModule(aidlImplementationGeneratorFactory, &nameProperties{
162		Name: proptools.StringPtr(cppModuleGen + "-generator"),
163	}, &aidlImplementationGeneratorProperties{
164		Lang:              lang,
165		AidlInterfaceName: i.ModuleBase.Name(),
166		Version:           version,
167		ModuleProperties: []interface{}{
168			&ccProperties{
169				Name:                      proptools.StringPtr(cppModuleGen),
170				Vendor_available:          vendorAvailable,
171				Odm_available:             odmAvailable,
172				Product_available:         productAvailable,
173				Host_supported:            hostSupported,
174				Defaults:                  []string{"aidl-cpp-module-defaults"},
175				Double_loadable:           i.properties.Double_loadable,
176				Generated_sources:         []string{cppSourceGen},
177				Generated_headers:         []string{cppSourceGen},
178				Export_generated_headers:  []string{cppSourceGen},
179				Shared_libs:               append(importExportDependencies, sharedLibDependency...),
180				Header_libs:               headerLibs,
181				Export_shared_lib_headers: importExportDependencies,
182				Sdk_version:               sdkVersion,
183				Stl:                       stl,
184				Cpp_std:                   cpp_std,
185				Cflags:                    append(addCflags, "-Wextra", "-Wall", "-Werror", "-Wextra-semi"),
186				Apex_available:            commonProperties.Apex_available,
187				Min_sdk_version:           minSdkVersion,
188				UseApexNameMacro:          true,
189				Target: ccTargetProperties{
190					// Currently necessary for host builds
191					// TODO(b/31559095): bionic on host should define this
192					// TODO(b/146436251): default isn't applied because the module is created
193					// in PreArchMutators, when import behavior becomes explicit, the logic can
194					// be moved back to LoadHook
195					Host: hostProperties{Cflags: []string{
196						"-D__INTRODUCED_IN(n)=",
197						"-D__assert(a,b,c)=",
198						// We want all the APIs to be available on the host.
199						"-D__ANDROID_API__=10000"}},
200					Darwin: perTargetProperties{Enabled: proptools.BoolPtr(false)}},
201				Tidy: proptools.BoolPtr(true),
202				// Do the tidy check only for the generated headers
203				Tidy_flags:            []string{"--header-filter=" + android.PathForOutput(mctx).String() + ".*"},
204				Tidy_checks_as_errors: []string{"*"},
205			}, &i.properties.VndkProperties,
206			&commonProperties.VndkProperties,
207			&overrideVndkProperties,
208		},
209	})
210
211	return cppModuleGen
212}
213
214func addJavaLibrary(mctx android.LoadHookContext, i *aidlInterface, version string) string {
215	javaSourceGen := i.versionedName(version) + "-java-source"
216	javaModuleGen := i.versionedName(version) + "-java"
217	srcs, aidlRoot := i.srcsForVersion(mctx, version)
218	if len(srcs) == 0 {
219		// This can happen when the version is about to be frozen; the version
220		// directory is created but API dump hasn't been copied there.
221		// Don't create a library for the yet-to-be-frozen version.
222		return ""
223	}
224
225	sdkVersion := i.properties.Backend.Java.Sdk_version
226	if !proptools.Bool(i.properties.Backend.Java.Platform_apis) && sdkVersion == nil {
227		// platform apis requires no default
228		sdkVersion = proptools.StringPtr("system_current")
229	}
230
231	mctx.CreateModule(aidlGenFactory, &nameProperties{
232		Name: proptools.StringPtr(javaSourceGen),
233	}, &aidlGenProperties{
234		Srcs:                  srcs,
235		AidlRoot:              aidlRoot,
236		IsToT:                 version == i.nextVersion(),
237		ImportsWithoutVersion: i.properties.ImportsWithoutVersion,
238		Stability:             i.properties.Stability,
239		Lang:                  langJava,
240		BaseName:              i.ModuleBase.Name(),
241		Version:               i.versionForAidlGenRule(version),
242		GenTrace:              proptools.Bool(i.properties.Gen_trace),
243		Unstable:              i.properties.Unstable,
244		Visibility:            srcsVisibility(mctx, langJava),
245		Flags:                 i.flagsForAidlGenRule(version),
246	})
247
248	mctx.CreateModule(aidlImplementationGeneratorFactory, &nameProperties{
249		Name: proptools.StringPtr(javaModuleGen + "-generator"),
250	}, &aidlImplementationGeneratorProperties{
251		Lang:              langJava,
252		AidlInterfaceName: i.ModuleBase.Name(),
253		Version:           version,
254		ModuleProperties: []interface{}{&javaProperties{
255			Name:            proptools.StringPtr(javaModuleGen),
256			Installable:     proptools.BoolPtr(true),
257			Defaults:        []string{"aidl-java-module-defaults"},
258			Sdk_version:     sdkVersion,
259			Platform_apis:   i.properties.Backend.Java.Platform_apis,
260			Srcs:            []string{":" + javaSourceGen},
261			Apex_available:  i.properties.Backend.Java.Apex_available,
262			Min_sdk_version: i.properties.Backend.Java.Min_sdk_version,
263		}},
264	})
265
266	return javaModuleGen
267}
268
269func addRustLibrary(mctx android.LoadHookContext, i *aidlInterface, version string) string {
270	rustSourceGen := i.versionedName(version) + "-rust-source"
271	rustModuleGen := i.versionedName(version) + "-rust"
272	srcs, aidlRoot := i.srcsForVersion(mctx, version)
273	if len(srcs) == 0 {
274		// This can happen when the version is about to be frozen; the version
275		// directory is created but API dump hasn't been copied there.
276		// Don't create a library for the yet-to-be-frozen version.
277		return ""
278	}
279
280	mctx.CreateModule(aidlGenFactory, &nameProperties{
281		Name: proptools.StringPtr(rustSourceGen),
282	}, &aidlGenProperties{
283		Srcs:                  srcs,
284		AidlRoot:              aidlRoot,
285		ImportsWithoutVersion: i.properties.ImportsWithoutVersion,
286		IsToT:                 version == i.nextVersion(),
287		Stability:             i.properties.Stability,
288		Lang:                  langRust,
289		BaseName:              i.ModuleBase.Name(),
290		Version:               i.versionForAidlGenRule(version),
291		Unstable:              i.properties.Unstable,
292		Visibility:            srcsVisibility(mctx, langRust),
293		Flags:                 i.flagsForAidlGenRule(version),
294	})
295
296	versionedRustName := fixRustName(i.versionedName(version))
297	rustCrateName := fixRustName(i.ModuleBase.Name())
298
299	mctx.CreateModule(aidlRustLibraryFactory, &rustProperties{
300		Name:           proptools.StringPtr(rustModuleGen),
301		Crate_name:     rustCrateName,
302		Stem:           proptools.StringPtr("lib" + versionedRustName),
303		Defaults:       []string{"aidl-rust-module-defaults"},
304		Host_supported: i.properties.Host_supported,
305		Apex_available: i.properties.Backend.Rust.Apex_available,
306		Target:         rustTargetProperties{Darwin: perTargetProperties{Enabled: proptools.BoolPtr(false)}},
307	}, &rust.SourceProviderProperties{
308		Source_stem: proptools.StringPtr(versionedRustName),
309	}, &aidlRustSourceProviderProperties{
310		SourceGen:         rustSourceGen,
311		Imports:           i.properties.Imports,
312		Version:           version,
313		AidlInterfaceName: i.ModuleBase.Name(),
314	})
315
316	return rustModuleGen
317}
318
319// This function returns module name with version. Assume that there is foo of which latest version is 2
320// Version -> Module name
321// "1"->foo-V1
322// "2"->foo-V2
323// "3"->foo-V3
324// And assume that there is 'bar' which is an 'unstable' interface.
325// ""->bar
326func (i *aidlInterface) versionedName(version string) string {
327	name := i.ModuleBase.Name()
328	if version == "" {
329		return name
330	}
331	return name + "-V" + version
332}
333
334func (i *aidlInterface) srcsForVersion(mctx android.LoadHookContext, version string) (srcs []string, aidlRoot string) {
335	if version == i.nextVersion() {
336		return i.properties.Srcs, i.properties.Local_include_dir
337	} else {
338		aidlRoot = filepath.Join(aidlApiDir, i.ModuleBase.Name(), version)
339		full_paths, err := mctx.GlobWithDeps(filepath.Join(mctx.ModuleDir(), aidlRoot, "**/*.aidl"), nil)
340		if err != nil {
341			panic(err)
342		}
343		for _, path := range full_paths {
344			// Here, we need path local to the module
345			srcs = append(srcs, strings.TrimPrefix(path, mctx.ModuleDir()+"/"))
346		}
347		return srcs, aidlRoot
348	}
349}
350
351func (i *aidlInterface) versionForAidlGenRule(version string) string {
352	if !i.hasVersion() {
353		return ""
354	}
355	return version
356}
357
358func (i *aidlInterface) flagsForAidlGenRule(version string) (flags []string) {
359	flags = append(flags, i.properties.Flags...)
360	// For ToT, turn on "-Weverything" (enable all warnings)
361	if version == i.nextVersion() {
362		flags = append(flags, "-Weverything")
363	}
364	return
365}
366
367func (i *aidlInterface) isModuleForVndk(version string) bool {
368	if i.properties.Vndk_use_version != nil {
369		if !i.hasVersion() {
370			panic("does not make sense, vndk_use_version specififed")
371		}
372		// Will be exactly one of the version numbers
373		return version == *i.properties.Vndk_use_version
374	}
375
376	// For an interface with no versions, this is the ToT interface.
377	if !i.hasVersion() {
378		return version == i.nextVersion()
379	}
380
381	return version == i.latestVersion()
382}
383
384// importing aidl_interface's version  | imported aidl_interface | imported aidl_interface's version
385// --------------------------------------------------------------------------------------------------
386// whatever                            | unstable                | unstable version
387// ToT version(including unstable)     | whatever                | ToT version(unstable if unstable)
388// otherwise                           | whatever                | the latest stable version
389// In the case that import specifies the version which it wants to use, use that version.
390func (i *aidlInterface) getImportWithVersion(version string, anImport string, config android.Config) string {
391	if hasVersionSuffix(anImport) {
392		return anImport
393	}
394	other := lookupInterface(anImport, config)
395	if proptools.Bool(other.properties.Unstable) {
396		return anImport
397	}
398	if version == i.nextVersion() || !other.hasVersion() {
399		return other.versionedName(other.nextVersion())
400	}
401	return other.versionedName(other.latestVersion())
402}
403
404func aidlImplementationGeneratorFactory() android.Module {
405	g := &aidlImplementationGenerator{}
406	g.AddProperties(&g.properties)
407	android.InitAndroidModule(g)
408	return g
409}
410
411type aidlImplementationGenerator struct {
412	android.ModuleBase
413	properties aidlImplementationGeneratorProperties
414}
415
416type aidlImplementationGeneratorProperties struct {
417	Lang              string
418	AidlInterfaceName string
419	Version           string
420	ModuleProperties  []interface{}
421}
422
423func (g *aidlImplementationGenerator) DepsMutator(ctx android.BottomUpMutatorContext) {
424}
425
426func (g *aidlImplementationGenerator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
427}
428
429func (g *aidlImplementationGenerator) GenerateImplementation(ctx android.TopDownMutatorContext) {
430	i := lookupInterface(g.properties.AidlInterfaceName, ctx.Config())
431	version := g.properties.Version
432	lang := g.properties.Lang
433	if g.properties.Lang == langJava {
434		imports := make([]string, len(i.properties.Imports))
435		for idx, anImport := range i.properties.Imports {
436			imports[idx] = i.getImportWithVersion(version, anImport, ctx.Config()) + "-" + langJava
437		}
438		if p, ok := g.properties.ModuleProperties[0].(*javaProperties); ok {
439			p.Static_libs = imports
440		}
441		ctx.CreateModule(java.LibraryFactory, g.properties.ModuleProperties...)
442	} else {
443		imports := make([]string, len(i.properties.Imports))
444		for idx, anImport := range i.properties.Imports {
445			imports[idx] = i.getImportWithVersion(version, anImport, ctx.Config()) + "-" + lang
446		}
447		if p, ok := g.properties.ModuleProperties[0].(*ccProperties); ok {
448			p.Shared_libs = append(p.Shared_libs, imports...)
449			p.Export_shared_lib_headers = append(p.Export_shared_lib_headers, imports...)
450		}
451		ctx.CreateModule(cc.LibraryFactory, g.properties.ModuleProperties...)
452	}
453}
454