From 2db1c3f1c432740f734917e04b2b847066c8d508 Mon Sep 17 00:00:00 2001 From: Artur Satayev Date: Wed, 8 Apr 2020 19:09:30 +0100 Subject: [PATCH] Check updatable APKs compile against managed SDKs. As a follow up, this property will be set to APKs participating in mainline program. Bug: 153333044 Test: m Change-Id: I6ea2f3c1d26992259e4e9e6a6d8cecf091d39c43 --- java/app.go | 18 ++++++++- java/app_test.go | 102 +++++++++++++++++++++++++++++++++++++++++++++++ java/java.go | 26 +++++++----- java/sdk.go | 21 ++++++++++ 4 files changed, 154 insertions(+), 13 deletions(-) diff --git a/java/app.go b/java/app.go index ae9cd5f11..ac5121a3d 100755 --- a/java/app.go +++ b/java/app.go @@ -110,6 +110,10 @@ type appProperties struct { PreventInstall bool `blueprint:"mutated"` HideFromMake bool `blueprint:"mutated"` IsCoverageVariant bool `blueprint:"mutated"` + + // Whether this app is considered mainline updatable or not. When set to true, this will enforce + // additional rules for making sure that the APK is truly updatable. Default is false. + Updatable *bool } // android_app properties that can be overridden by override_android_app @@ -249,11 +253,21 @@ func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleCon } func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { - a.checkPlatformAPI(ctx) - a.checkSdkVersion(ctx) + a.checkAppSdkVersions(ctx) a.generateAndroidBuildActions(ctx) } +func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { + if Bool(a.appProperties.Updatable) { + if !a.sdkVersion().stable() { + ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.sdkVersion()) + } + } + + a.checkPlatformAPI(ctx) + a.checkSdkVersions(ctx) +} + // Returns true if the native libraries should be stored in the APK uncompressed and the // extractNativeLibs application flag should be set to false in the manifest. func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool { diff --git a/java/app_test.go b/java/app_test.go index 43696dbb8..7b04e4629 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -264,6 +264,108 @@ func TestAndroidAppLinkType(t *testing.T) { `) } +func TestUpdatableApps(t *testing.T) { + testCases := []struct { + name string + bp string + expectedError string + }{ + { + name: "Stable public SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "29", + updatable: true, + }`, + }, + { + name: "Stable system SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "system_29", + updatable: true, + }`, + }, + { + name: "Current public SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + updatable: true, + }`, + }, + { + name: "Current system SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "system_current", + updatable: true, + }`, + }, + { + name: "Current module SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "module_current", + updatable: true, + }`, + }, + { + name: "Current core SDK", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "core_current", + updatable: true, + }`, + }, + { + name: "No Platform APIs", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + platform_apis: true, + updatable: true, + }`, + expectedError: "Updatable apps must use stable SDKs", + }, + { + name: "No Core Platform APIs", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "core_platform", + updatable: true, + }`, + expectedError: "Updatable apps must use stable SDKs", + }, + { + name: "No unspecified APIs", + bp: `android_app { + name: "foo", + srcs: ["a.java"], + updatable: true, + }`, + expectedError: "Updatable apps must use stable SDK", + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + if test.expectedError == "" { + testJava(t, test.bp) + } else { + testJavaError(t, test.expectedError, test.bp) + } + }) + } +} + func TestResourceDirs(t *testing.T) { testCases := []struct { name string diff --git a/java/java.go b/java/java.go index a8ca3ffef..61974f5af 100644 --- a/java/java.go +++ b/java/java.go @@ -86,7 +86,7 @@ func RegisterJavaBuildComponents(ctx android.RegistrationContext) { ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory) } -func (j *Module) checkSdkVersion(ctx android.ModuleContext) { +func (j *Module) checkSdkVersions(ctx android.ModuleContext) { if j.SocSpecific() || j.DeviceSpecific() || (j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) { if sc, ok := ctx.Module().(sdkContext); ok { @@ -96,6 +96,18 @@ func (j *Module) checkSdkVersion(ctx android.ModuleContext) { } } } + + ctx.VisitDirectDeps(func(module android.Module) { + tag := ctx.OtherModuleDependencyTag(module) + switch module.(type) { + // TODO(satayev): cover other types as well, e.g. imports + case *Library, *AndroidLibrary: + switch tag { + case bootClasspathTag, libTag, staticLibTag, java9LibTag: + checkLinkType(ctx, j, module.(linkTypeContext), tag.(dependencyTag)) + } + } + }) } func (j *Module) checkPlatformAPI(ctx android.ModuleContext) { @@ -898,15 +910,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { // Handled by AndroidApp.collectAppDeps return } - switch module.(type) { - case *Library, *AndroidLibrary: - if to, ok := module.(linkTypeContext); ok { - switch tag { - case bootClasspathTag, libTag, staticLibTag: - checkLinkType(ctx, j, to, tag.(dependencyTag)) - } - } - } + switch dep := module.(type) { case SdkLibraryDependency: switch tag { @@ -1818,7 +1822,7 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo } func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { - j.checkSdkVersion(ctx) + j.checkSdkVersions(ctx) j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter) diff --git a/java/sdk.go b/java/sdk.go index 0e132d399..92076f4b5 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -147,6 +147,10 @@ type sdkSpec struct { raw string } +func (s sdkSpec) String() string { + return fmt.Sprintf("%s_%s", s.kind, s.version) +} + // valid checks if this sdkSpec is well-formed. Note however that true doesn't mean that the // specified SDK actually exists. func (s sdkSpec) valid() bool { @@ -158,6 +162,23 @@ func (s sdkSpec) specified() bool { return s.valid() && s.kind != sdkPrivate } +// whether the API surface is managed and versioned, i.e. has .txt file that +// get frozen on SDK freeze and changes get reviewed by API council. +func (s sdkSpec) stable() bool { + if !s.specified() { + return false + } + switch s.kind { + case sdkCore, sdkPublic, sdkSystem, sdkModule, sdkSystemServer: + return true + case sdkNone, sdkCorePlatform, sdkTest, sdkPrivate: + return false + default: + panic(fmt.Errorf("unknown sdkKind=%v", s.kind)) + } + return false +} + // prebuiltSdkAvailableForUnbundledBuilt tells whether this sdkSpec can have a prebuilt SDK // that can be used for unbundled builds. func (s sdkSpec) prebuiltSdkAvailableForUnbundledBuild() bool {