1// Copyright 2018 Google Inc. All rights reserved.
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 java
16
17import (
18	"fmt"
19	"strings"
20
21	"github.com/google/blueprint"
22
23	"android/soong/android"
24)
25
26var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
27	blueprint.RuleParams{
28		Command: `${config.ManifestFixerCmd} ` +
29			`--minSdkVersion ${minSdkVersion} ` +
30			`--targetSdkVersion ${targetSdkVersion} ` +
31			`--raise-min-sdk-version ` +
32			`$args $in $out`,
33		CommandDeps: []string{"${config.ManifestFixerCmd}"},
34	},
35	"minSdkVersion", "targetSdkVersion", "args")
36
37var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
38	blueprint.RuleParams{
39		Command:     `${config.ManifestMergerCmd} $args --main $in $libs --out $out`,
40		CommandDeps: []string{"${config.ManifestMergerCmd}"},
41	},
42	"args", "libs")
43
44// These two libs are added as optional dependencies (<uses-library> with
45// android:required set to false). This is because they haven't existed in pre-P
46// devices, but classes in them were in bootclasspath jars, etc. So making them
47// hard dependencies (android:required=true) would prevent apps from being
48// installed to such legacy devices.
49var optionalUsesLibs = []string{
50	"android.test.base",
51	"android.test.mock",
52}
53
54// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
55func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries []string,
56	isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
57
58	var args []string
59	if isLibrary {
60		args = append(args, "--library")
61	} else {
62		minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersion(ctx)
63		if err != nil {
64			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
65		}
66		if minSdkVersion >= 23 {
67			args = append(args, fmt.Sprintf("--extract-native-libs=%v", !useEmbeddedNativeLibs))
68		} else if useEmbeddedNativeLibs {
69			ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it",
70				minSdkVersion)
71		}
72	}
73
74	if usesNonSdkApis {
75		args = append(args, "--uses-non-sdk-api")
76	}
77
78	if useEmbeddedDex {
79		args = append(args, "--use-embedded-dex")
80	}
81
82	for _, usesLib := range sdkLibraries {
83		if inList(usesLib, optionalUsesLibs) {
84			args = append(args, "--optional-uses-library", usesLib)
85		} else {
86			args = append(args, "--uses-library", usesLib)
87		}
88	}
89
90	if hasNoCode {
91		args = append(args, "--has-no-code")
92	}
93
94	if loggingParent != "" {
95		args = append(args, "--logging-parent", loggingParent)
96	}
97	var deps android.Paths
98	targetSdkVersion, err := sdkContext.targetSdkVersion().effectiveVersionString(ctx)
99	if err != nil {
100		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
101	}
102	if UseApiFingerprint(ctx) {
103		targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
104		deps = append(deps, ApiFingerprintPath(ctx))
105	}
106
107	minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersionString(ctx)
108	if err != nil {
109		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
110	}
111	if UseApiFingerprint(ctx) {
112		minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
113		deps = append(deps, ApiFingerprintPath(ctx))
114	}
115
116	fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
117	if err != nil {
118		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
119	}
120	ctx.Build(pctx, android.BuildParams{
121		Rule:        manifestFixerRule,
122		Description: "fix manifest",
123		Input:       manifest,
124		Implicits:   deps,
125		Output:      fixedManifest,
126		Args: map[string]string{
127			"minSdkVersion":    minSdkVersion,
128			"targetSdkVersion": targetSdkVersion,
129			"args":             strings.Join(args, " "),
130		},
131	})
132
133	return fixedManifest.WithoutRel()
134}
135
136func manifestMerger(ctx android.ModuleContext, manifest android.Path, staticLibManifests android.Paths,
137	isLibrary bool) android.Path {
138
139	var args string
140	if !isLibrary {
141		// Follow Gradle's behavior, only pass --remove-tools-declarations when merging app manifests.
142		args = "--remove-tools-declarations"
143	}
144
145	mergedManifest := android.PathForModuleOut(ctx, "manifest_merger", "AndroidManifest.xml")
146	ctx.Build(pctx, android.BuildParams{
147		Rule:        manifestMergerRule,
148		Description: "merge manifest",
149		Input:       manifest,
150		Implicits:   staticLibManifests,
151		Output:      mergedManifest,
152		Args: map[string]string{
153			"libs": android.JoinWithPrefix(staticLibManifests.Strings(), "--libs "),
154			"args": args,
155		},
156	})
157
158	return mergedManifest.WithoutRel()
159}
160