Merge "Don't check elf properties of python prebuilts" into rvc-dev
diff --git a/android/mutator.go b/android/mutator.go
index a46d4be..10a815a 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -103,7 +103,7 @@
 	registerPathDepsMutator,
 	RegisterPrebuiltsPostDepsMutators,
 	RegisterVisibilityRuleEnforcer,
-	registerNeverallowMutator,
+	RegisterNeverallowMutator,
 	RegisterOverridePostDepsMutators,
 }
 
diff --git a/android/neverallow.go b/android/neverallow.go
index ea307ca..4d3a16f 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -42,7 +42,7 @@
 //     counts as a match
 // - it has none of the "Without" properties matched (same rules as above)
 
-func registerNeverallowMutator(ctx RegisterMutatorsContext) {
+func RegisterNeverallowMutator(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
 }
 
@@ -215,7 +215,7 @@
 }
 
 type ValueMatcher interface {
-	test(string) bool
+	Test(string) bool
 	String() string
 }
 
@@ -223,7 +223,7 @@
 	expected string
 }
 
-func (m *equalMatcher) test(value string) bool {
+func (m *equalMatcher) Test(value string) bool {
 	return m.expected == value
 }
 
@@ -234,7 +234,7 @@
 type anyMatcher struct {
 }
 
-func (m *anyMatcher) test(value string) bool {
+func (m *anyMatcher) Test(value string) bool {
 	return true
 }
 
@@ -248,7 +248,7 @@
 	prefix string
 }
 
-func (m *startsWithMatcher) test(value string) bool {
+func (m *startsWithMatcher) Test(value string) bool {
 	return strings.HasPrefix(value, m.prefix)
 }
 
@@ -260,7 +260,7 @@
 	re *regexp.Regexp
 }
 
-func (m *regexMatcher) test(value string) bool {
+func (m *regexMatcher) Test(value string) bool {
 	return m.re.MatchString(value)
 }
 
@@ -529,7 +529,7 @@
 		}
 
 		check := func(value string) bool {
-			return prop.matcher.test(value)
+			return prop.matcher.Test(value)
 		}
 
 		if matchValue(propertiesValue, check) {
@@ -586,6 +586,6 @@
 // Overrides the default neverallow rules for the supplied config.
 //
 // For testing only.
-func setTestNeverallowRules(config Config, testRules []Rule) {
+func SetTestNeverallowRules(config Config, testRules []Rule) {
 	config.Once(neverallowRulesKey, func() interface{} { return testRules })
 }
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 7992936..83b2250 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -269,7 +269,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			// If the test has its own rules then use them instead of the default ones.
 			if test.rules != nil {
-				setTestNeverallowRules(config, test.rules)
+				SetTestNeverallowRules(config, test.rules)
 			}
 			_, errs := testNeverallow(config)
 			CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
@@ -283,7 +283,7 @@
 	ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
 	ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
 	ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
-	ctx.PostDepsMutators(registerNeverallowMutator)
+	ctx.PostDepsMutators(RegisterNeverallowMutator)
 	ctx.Register(config)
 
 	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
diff --git a/java/app.go b/java/app.go
index 1ad81fe..55a758f 100755
--- a/java/app.go
+++ b/java/app.go
@@ -110,6 +110,10 @@
 	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
@@ -242,11 +246,21 @@
 }
 
 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 c259fe2..4458d4e 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -264,6 +264,108 @@
 	`)
 }
 
+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 d67ff87..7956813 100644
--- a/java/java.go
+++ b/java/java.go
@@ -80,7 +80,7 @@
 	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 {
@@ -90,6 +90,18 @@
 			}
 		}
 	}
+
+	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) {
@@ -892,15 +904,7 @@
 			// 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 {
@@ -1817,7 +1821,7 @@
 }
 
 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 efd1465..be5e512 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -148,6 +148,10 @@
 	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 {
@@ -159,6 +163,23 @@
 	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 {