diff --git a/java/androidmk.go b/java/androidmk.go index 865c1201a..d2e0f2e63 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -79,6 +79,10 @@ func (library *Library) AndroidMk() android.AndroidMkData { fmt.Fprintln(w, "LOCAL_EXPORT_SDK_LIBRARIES :=", strings.Join(library.exportedSdkLibs, " ")) } + if len(library.additionalCheckedModules) != 0 { + fmt.Fprintln(w, "LOCAL_ADDITIONAL_CHECKED_MODULE +=", strings.Join(library.additionalCheckedModules.Strings(), " ")) + } + // Temporary hack: export sources used to compile framework.jar to Make // to be used for droiddoc // TODO(ccross): remove this once droiddoc is in soong diff --git a/java/builder.go b/java/builder.go index ce9a5ee87..338cd52ed 100644 --- a/java/builder.go +++ b/java/builder.go @@ -113,6 +113,15 @@ var ( }, "rulesFile") + packageCheck = pctx.AndroidStaticRule("packageCheck", + blueprint.RuleParams{ + Command: "rm -f $out && " + + "${config.PackageCheckCmd} $in $packages && " + + "touch $out", + CommandDeps: []string{"${config.PackageCheckCmd}"}, + }, + "packages") + jetifier = pctx.AndroidStaticRule("jetifier", blueprint.RuleParams{ Command: "${config.JavaCmd} -jar ${config.JetifierJar} -l error -o $out -i $in", @@ -356,6 +365,19 @@ func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath, }) } +func CheckJarPackages(ctx android.ModuleContext, outputFile android.WritablePath, + classesJar android.Path, permittedPackages []string) { + ctx.Build(pctx, android.BuildParams{ + Rule: packageCheck, + Description: "packageCheck", + Output: outputFile, + Input: classesJar, + Args: map[string]string{ + "packages": strings.Join(permittedPackages, " "), + }, + }) +} + func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) { ctx.Build(pctx, android.BuildParams{ diff --git a/java/config/config.go b/java/config/config.go index 3452a1db6..46cd361d7 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -91,6 +91,7 @@ func init() { pctx.SourcePathVariable("GenKotlinBuildFileCmd", "build/soong/scripts/gen-kotlin-build-file.sh") pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh") + pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh") pctx.HostBinToolVariable("ExtractJarPackagesCmd", "extract_jar_packages") pctx.HostBinToolVariable("SoongZipCmd", "soong_zip") pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips") diff --git a/java/java.go b/java/java.go index 0417dee82..bf62578f8 100644 --- a/java/java.go +++ b/java/java.go @@ -117,6 +117,10 @@ type CompilerProperties struct { // If set to true, include sources used to compile the module in to the final jar Include_srcs *bool + // If not empty, classes are restricted to the specified packages and their sub-packages. + // This restriction is checked after applying jarjar rules and including static libs. + Permitted_packages []string + // List of modules to use as annotation processors Plugins []string @@ -320,6 +324,9 @@ type Module struct { // expanded Jarjar_rules expandJarjarRules android.Path + // list of additional targets for checkbuild + additionalCheckedModules android.Paths + hiddenAPI dexpreopter } @@ -1196,6 +1203,19 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars ...android.Path return } } + + // Check package restrictions if necessary. + if len(j.properties.Permitted_packages) > 0 { + // Check packages and copy to package-checked file. + pkgckFile := android.PathForModuleOut(ctx, "package-check.stamp") + CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages) + j.additionalCheckedModules = append(j.additionalCheckedModules, pkgckFile) + + if ctx.Failed() { + return + } + } + j.implementationJarFile = outputFile if j.headerJarFile == nil { j.headerJarFile = j.implementationJarFile diff --git a/scripts/package-check.sh b/scripts/package-check.sh new file mode 100755 index 000000000..f982e8244 --- /dev/null +++ b/scripts/package-check.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# +# Copyright (C) 2019 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. + +set -e + +if [[ $# -le 1 ]]; then + cat < +Checks that the class files in the are in the or +sub-packages. +EOF + exit 1 +fi + +jar_file=$1 +shift +if [[ ! -f ${jar_file} ]]; then + echo "jar file \"${jar_file}\" does not exist." + exit 1 +fi + +prefixes=() +while [[ $# -ge 1 ]]; do + package="$1" + if [[ "${package}" = */* ]]; then + echo "Invalid package \"${package}\". Use dot notation for packages." + exit 1 + fi + # Transform to a slash-separated path and add a trailing slash to enforce + # package name boundary. + prefixes+=("${package//\./\/}/") + shift +done + +# Get the file names from the jar file. +zip_contents=`zipinfo -1 $jar_file` + +# Check all class file names against the expected prefixes. +old_ifs=${IFS} +IFS=$'\n' +for zip_entry in ${zip_contents}; do + # Check the suffix. + if [[ "${zip_entry}" = *.class ]]; then + # Match against prefixes. + found=false + for prefix in ${prefixes[@]}; do + if [[ "${zip_entry}" = "${prefix}"* ]]; then + found=true + break + fi + done + if [[ "${found}" == "false" ]]; then + echo "Class file ${zip_entry} is outside specified packages." + exit 1 + fi + fi +done +IFS=${old_ifs}