Browse Source

Implement sysprop_library API stability check

sysprop_library now checks the API stability itself, cutting dependency
on java_sdk_library. Under the directory {module_dir}/api,
{module_name}-current.txt and {module_name}-latest.txt hold API
signatures.

When sysprop_library is built, or a user run "m {module_name}-check-api"
command, API check is performed. First, current.txt must have exactly
same signature with built sysprop_library module. Second, current.txt
must be compatible with latest.txt.

Build system emits a handy error message to generate/update those API
files, in case of missing or mismatching. Also, a script file for
freezing API files is introduced.

Bug: 131637873
Test: 1) m && boot blueline
Test: 2) m {sysprop_library} performs API check
Test: 3) manual test for check-api, freezing api
Change-Id: I9d25f5dc64299e666527ca8e23d7233966901c4e
Inseob Kim 4 years ago
parent
commit
093f0eb133

+ 1 - 3
cc/library.go

@@ -894,9 +894,7 @@ func (library *libraryDecorator) link(ctx ModuleContext,
 			isVendor := ctx.useVndk()
 			isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
 
-			usePublic := isProduct || (isOwnerPlatform == isVendor)
-
-			if usePublic {
+			if !ctx.inRecovery() && (isProduct || (isOwnerPlatform == isVendor)) {
 				dir = android.PathForModuleGen(ctx, "sysprop/public", "include").String()
 			}
 		}

+ 2 - 1
cc/sysprop.go

@@ -21,6 +21,7 @@ import (
 )
 
 type syspropLibraryInterface interface {
+	BaseModuleName() string
 	CcModuleName() string
 }
 
@@ -42,6 +43,6 @@ func SyspropMutator(mctx android.BottomUpMutatorContext) {
 		syspropImplLibrariesLock.Lock()
 		defer syspropImplLibrariesLock.Unlock()
 
-		syspropImplLibraries[mctx.ModuleName()] = m.CcModuleName()
+		syspropImplLibraries[m.BaseModuleName()] = m.CcModuleName()
 	}
 }

+ 0 - 3
java/droiddoc.go

@@ -481,9 +481,6 @@ func (j *Javadoc) genSources(ctx android.ModuleContext, srcFiles android.Paths,
 		case ".aidl":
 			javaFile := genAidl(ctx, srcFile, flags.aidlFlags, flags.aidlDeps)
 			outSrcFiles = append(outSrcFiles, javaFile)
-		case ".sysprop":
-			javaFile := genSysprop(ctx, srcFile)
-			outSrcFiles = append(outSrcFiles, javaFile)
 		default:
 			outSrcFiles = append(outSrcFiles, srcFile)
 		}

+ 27 - 4
java/gen.go

@@ -53,13 +53,13 @@ var (
 	sysprop = pctx.AndroidStaticRule("sysprop",
 		blueprint.RuleParams{
 			Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
-				`$syspropCmd --java-output-dir $out.tmp $in && ` +
+				`$syspropCmd --scope $scope --java-output-dir $out.tmp $in && ` +
 				`${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
 			CommandDeps: []string{
 				"$syspropCmd",
 				"${config.SoongZipCmd}",
 			},
-		})
+		}, "scope")
 )
 
 func genAidl(ctx android.ModuleContext, aidlFile android.Path, aidlFlags string, deps android.Paths) android.Path {
@@ -98,7 +98,7 @@ func genLogtags(ctx android.ModuleContext, logtagsFile android.Path) android.Pat
 	return javaFile
 }
 
-func genSysprop(ctx android.ModuleContext, syspropFile android.Path) android.Path {
+func genSysprop(ctx android.ModuleContext, syspropFile android.Path, scope string) android.Path {
 	srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
 
 	ctx.Build(pctx, android.BuildParams{
@@ -106,6 +106,9 @@ func genSysprop(ctx android.ModuleContext, syspropFile android.Path) android.Pat
 		Description: "sysprop_java " + syspropFile.Rel(),
 		Output:      srcJarFile,
 		Input:       syspropFile,
+		Args: map[string]string{
+			"scope": scope,
+		},
 	})
 
 	return srcJarFile
@@ -129,7 +132,27 @@ func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths,
 			srcJarFile := genProto(ctx, srcFile, flags.proto)
 			outSrcFiles = append(outSrcFiles, srcJarFile)
 		case ".sysprop":
-			srcJarFile := genSysprop(ctx, srcFile)
+			// internal scope contains all properties
+			// public scope only contains public properties
+			// use public if the owner is different from client
+			scope := "internal"
+			if j.properties.Sysprop.Platform != nil {
+				isProduct := ctx.ProductSpecific()
+				isVendor := ctx.SocSpecific()
+				isOwnerPlatform := Bool(j.properties.Sysprop.Platform)
+
+				if isProduct {
+					// product can't own any sysprop_library now, so product must use public scope
+					scope = "public"
+				} else if isVendor && !isOwnerPlatform {
+					// vendor and odm can't use system's internal property.
+					scope = "public"
+				}
+
+				// We don't care about clients under system.
+				// They can't use sysprop_library owned by other partitions.
+			}
+			srcJarFile := genSysprop(ctx, srcFile, scope)
 			outSrcFiles = append(outSrcFiles, srcJarFile)
 		default:
 			outSrcFiles = append(outSrcFiles, srcFile)

+ 4 - 1
java/java.go

@@ -169,6 +169,10 @@ type CompilerProperties struct {
 		Output_params []string
 	}
 
+	Sysprop struct {
+		Platform *bool
+	} `blueprint:"mutated"`
+
 	Instrument bool `blueprint:"mutated"`
 
 	// List of files to include in the META-INF/services folder of the resulting jar.
@@ -999,7 +1003,6 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB
 }
 
 func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
-
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
 	deps := j.collectDeps(ctx)

+ 39 - 0
scripts/freeze-sysprop-api-files.sh

@@ -0,0 +1,39 @@
+#!/bin/bash -e
+
+# 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.
+
+# This script freezes APIs of a sysprop_library after checking compatibility
+# between latest API and current API.
+#
+# Usage: freeze-sysprop-api-files.sh <modulePath> <moduleName>
+#
+# <modulePath>: the directory, either relative or absolute, which holds the
+# Android.bp file defining sysprop_library.
+#
+# <moduleName>: the name of sysprop_library to freeze API.
+#
+# Example:
+# $ . build/envsetup.sh && lunch aosp_arm64-user
+# $ . build/soong/scripts/freeze-sysprop-api-files.sh \
+#       system/libsysprop/srcs PlatformProperties
+
+if [[ -z "$1" || -z "$2" ]]; then
+  echo "usage: $0 <modulePath> <moduleName>" >&2
+  exit 1
+fi
+
+api_dir=$1/api
+
+m "$2-check-api" && cp -f "${api_dir}/$2-current.txt" "${api_dir}/$2-latest.txt"

+ 26 - 0
scripts/gen-sysprop-api-files.sh

@@ -0,0 +1,26 @@
+#!/bin/bash -e
+
+# 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.
+
+if [[ -z "$1" || -z "$2" ]]; then
+  echo "usage: $0 <modulePath> <moduleName>" >&2
+  exit 1
+fi
+
+api_dir=$1/api
+
+mkdir -p "$api_dir"
+touch "${api_dir}/$2-current.txt"
+touch "${api_dir}/$2-latest.txt"

+ 192 - 27
sysprop/sysprop_library.go

@@ -15,12 +15,16 @@
 package sysprop
 
 import (
-	"android/soong/android"
-	"android/soong/cc"
-	"android/soong/java"
+	"fmt"
+	"io"
+	"path"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/java"
 )
 
 type dependencyTag struct {
@@ -29,10 +33,14 @@ type dependencyTag struct {
 }
 
 type syspropLibrary struct {
-	java.SdkLibrary
+	android.ModuleBase
 
-	commonProperties         commonProperties
-	syspropLibraryProperties syspropLibraryProperties
+	properties syspropLibraryProperties
+
+	checkApiFileTimeStamp android.WritablePath
+	latestApiFile         android.Path
+	currentApiFile        android.Path
+	dumpedApiFile         android.WritablePath
 }
 
 type syspropLibraryProperties struct {
@@ -42,17 +50,22 @@ type syspropLibraryProperties struct {
 
 	// list of package names that will be documented and publicized as API
 	Api_packages []string
-}
 
-type commonProperties struct {
-	Srcs               []string
-	Recovery           *bool
+	// If set to true, allow this module to be dexed and installed on devices.
+	Installable *bool
+
+	// Make this module available when building for recovery
 	Recovery_available *bool
-	Vendor_available   *bool
+
+	// Make this module available when building for vendor
+	Vendor_available *bool
+
+	// list of .sysprop files which defines the properties.
+	Srcs []string `android:"path"`
 }
 
 var (
-	Bool         = proptools.Bool
+	pctx         = android.NewPackageContext("android/soong/sysprop")
 	syspropCcTag = dependencyTag{name: "syspropCc"}
 )
 
@@ -60,56 +73,166 @@ func init() {
 	android.RegisterModuleType("sysprop_library", syspropLibraryFactory)
 }
 
+func (m *syspropLibrary) Name() string {
+	return m.BaseModuleName() + "_sysprop_library"
+}
+
 func (m *syspropLibrary) CcModuleName() string {
-	return "lib" + m.Name()
+	return "lib" + m.BaseModuleName()
+}
+
+func (m *syspropLibrary) BaseModuleName() string {
+	return m.ModuleBase.Name()
+}
+
+func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", m.BaseModuleName()+"-current.txt")
+	m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", m.BaseModuleName()+"-latest.txt")
+
+	// dump API rule
+	rule := android.NewRuleBuilder()
+	m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt")
+	rule.Command().
+		BuiltTool(ctx, "sysprop_api_dump").
+		Output(m.dumpedApiFile).
+		Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
+	rule.Build(pctx, ctx, m.BaseModuleName()+"_api_dump", m.BaseModuleName()+" api dump")
+
+	// check API rule
+	rule = android.NewRuleBuilder()
+
+	// 1. current.txt <-> api_dump.txt
+	msg := fmt.Sprintf(`\n******************************\n`+
+		`API of sysprop_library %s doesn't match with current.txt\n`+
+		`Please update current.txt by:\n`+
+		`rm -rf %q && cp -f %q %q\n`+
+		`******************************\n`, m.BaseModuleName(),
+		m.currentApiFile.String(), m.dumpedApiFile.String(), m.currentApiFile.String())
+
+	rule.Command().
+		Text("( cmp").Flag("-s").
+		Input(m.dumpedApiFile).
+		Input(m.currentApiFile).
+		Text("|| ( echo").Flag("-e").
+		Flag(`"` + msg + `"`).
+		Text("; exit 38) )")
+
+	// 2. current.txt <-> latest.txt
+	msg = fmt.Sprintf(`\n******************************\n`+
+		`API of sysprop_library %s doesn't match with latest version\n`+
+		`Please fix the breakage and rebuild.\n`+
+		`******************************\n`, m.BaseModuleName())
+
+	rule.Command().
+		Text("( ").
+		BuiltTool(ctx, "sysprop_api_checker").
+		Input(m.latestApiFile).
+		Input(m.currentApiFile).
+		Text(" || ( echo").Flag("-e").
+		Flag(`"` + msg + `"`).
+		Text("; exit 38) )")
+
+	m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp")
+
+	rule.Command().
+		Text("touch").
+		Output(m.checkApiFileTimeStamp)
+
+	rule.Build(pctx, ctx, m.BaseModuleName()+"_check_api", m.BaseModuleName()+" check api")
 }
 
+func (m *syspropLibrary) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			// sysprop_library module itself is defined as a FAKE module to perform API check.
+			// Actual implementation libraries are created on LoadHookMutator
+			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+			fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name())
+			fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
+			fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
+			fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n")
+			fmt.Fprintf(w, "$(LOCAL_BUILT_MODULE): %s\n", m.checkApiFileTimeStamp.String())
+			fmt.Fprintf(w, "\ttouch $@\n\n")
+			fmt.Fprintf(w, ".PHONY: %s-check-api\n\n", name)
+
+			// check API rule
+			fmt.Fprintf(w, "%s-check-api: %s\n\n", name, m.checkApiFileTimeStamp.String())
+
+			// "make {sysprop_library}" should also build the C++ library
+			fmt.Fprintf(w, "%s: %s\n\n", name, m.CcModuleName())
+		}}
+}
+
+// sysprop_library creates schematized APIs from sysprop description files (.sysprop).
+// Both Java and C++ modules can link against sysprop_library, and API stability check
+// against latest APIs (see build/soong/scripts/freeze-sysprop-api-files.sh)
+// is performed.
 func syspropLibraryFactory() android.Module {
 	m := &syspropLibrary{}
 
 	m.AddProperties(
-		&m.commonProperties,
-		&m.syspropLibraryProperties,
+		&m.properties,
 	)
-	m.InitSdkLibraryProperties()
-	m.SetNoDist()
-	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, "common")
+	android.InitAndroidModule(m)
 	android.AddLoadHook(m, func(ctx android.LoadHookContext) { syspropLibraryHook(ctx, m) })
-	android.AddLoadHook(m, func(ctx android.LoadHookContext) { m.SdkLibrary.CreateInternalModules(ctx) })
 	return m
 }
 
 func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
-	if len(m.commonProperties.Srcs) == 0 {
+	if len(m.properties.Srcs) == 0 {
 		ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
 	}
 
-	if len(m.syspropLibraryProperties.Api_packages) == 0 {
-		ctx.PropertyErrorf("api_packages", "sysprop_library must specify api_packages")
+	missing_api := false
+
+	for _, txt := range []string{"-current.txt", "-latest.txt"} {
+		path := path.Join(ctx.ModuleDir(), "api", m.BaseModuleName()+txt)
+		file := android.ExistentPathForSource(ctx, path)
+		if !file.Valid() {
+			ctx.ModuleErrorf("API file %#v doesn't exist", path)
+			missing_api = true
+		}
+	}
+
+	if missing_api {
+		script := "build/soong/scripts/gen-sysprop-api-files.sh"
+		p := android.ExistentPathForSource(ctx, script)
+
+		if !p.Valid() {
+			panic(fmt.Sprintf("script file %s doesn't exist", script))
+		}
+
+		ctx.ModuleErrorf("One or more api files are missing. "+
+			"You can create them by:\n"+
+			"%s %q %q", script, ctx.ModuleDir(), m.BaseModuleName())
+		return
 	}
 
 	socSpecific := ctx.SocSpecific()
 	deviceSpecific := ctx.DeviceSpecific()
 	productSpecific := ctx.ProductSpecific()
 
-	owner := m.syspropLibraryProperties.Property_owner
+	owner := m.properties.Property_owner
+	stub := "sysprop-library-stub-"
 
 	switch owner {
 	case "Platform":
 		// Every partition can access platform-defined properties
-		break
+		stub += "platform"
 	case "Vendor":
 		// System can't access vendor's properties
 		if !socSpecific && !deviceSpecific && !productSpecific {
 			ctx.ModuleErrorf("None of soc_specific, device_specific, product_specific is true. " +
 				"System can't access sysprop_library owned by Vendor")
 		}
+		stub += "vendor"
 	case "Odm":
 		// Only vendor can access Odm-defined properties
 		if !socSpecific && !deviceSpecific {
 			ctx.ModuleErrorf("Neither soc_speicifc nor device_specific is true. " +
 				"Odm-defined properties should be accessed only in Vendor or Odm")
 		}
+		stub += "vendor"
 	default:
 		ctx.PropertyErrorf("property_owner",
 			"Unknown value %s: must be one of Platform, Vendor or Odm", owner)
@@ -117,17 +240,23 @@ func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
 
 	ccProps := struct {
 		Name             *string
+		Srcs             []string
 		Soc_specific     *bool
 		Device_specific  *bool
 		Product_specific *bool
 		Sysprop          struct {
 			Platform *bool
 		}
-		Header_libs []string
-		Shared_libs []string
+		Header_libs        []string
+		Shared_libs        []string
+		Required           []string
+		Recovery           *bool
+		Recovery_available *bool
+		Vendor_available   *bool
 	}{}
 
 	ccProps.Name = proptools.StringPtr(m.CcModuleName())
+	ccProps.Srcs = m.properties.Srcs
 	ccProps.Soc_specific = proptools.BoolPtr(socSpecific)
 	ccProps.Device_specific = proptools.BoolPtr(deviceSpecific)
 	ccProps.Product_specific = proptools.BoolPtr(productSpecific)
@@ -135,5 +264,41 @@ func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
 	ccProps.Header_libs = []string{"libbase_headers"}
 	ccProps.Shared_libs = []string{"liblog"}
 
-	ctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &m.commonProperties, &ccProps)
+	// add sysprop_library module to perform check API
+	ccProps.Required = []string{m.Name()}
+	ccProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform")
+	ccProps.Recovery_available = m.properties.Recovery_available
+	ccProps.Vendor_available = m.properties.Vendor_available
+
+	ctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProps)
+
+	javaProps := struct {
+		Name             *string
+		Srcs             []string
+		Soc_specific     *bool
+		Device_specific  *bool
+		Product_specific *bool
+		Sysprop          struct {
+			Platform *bool
+		}
+		Required    []string
+		Sdk_version *string
+		Installable *bool
+		Libs        []string
+	}{}
+
+	javaProps.Name = proptools.StringPtr(m.BaseModuleName())
+	javaProps.Srcs = m.properties.Srcs
+	javaProps.Soc_specific = proptools.BoolPtr(socSpecific)
+	javaProps.Device_specific = proptools.BoolPtr(deviceSpecific)
+	javaProps.Product_specific = proptools.BoolPtr(productSpecific)
+	javaProps.Installable = m.properties.Installable
+
+	// add sysprop_library module to perform check API
+	javaProps.Required = []string{m.Name()}
+	javaProps.Sdk_version = proptools.StringPtr("core_current")
+	javaProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform")
+	javaProps.Libs = []string{stub}
+
+	ctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory), &javaProps)
 }

+ 49 - 50
sysprop/sysprop_test.go

@@ -57,16 +57,11 @@ func testContext(config android.Config, bp string,
 
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(java.AndroidAppFactory))
-	ctx.RegisterModuleType("droiddoc_template", android.ModuleFactoryAdaptor(java.ExportedDroiddocDirFactory))
 	ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(java.LibraryFactory))
 	ctx.RegisterModuleType("java_system_modules", android.ModuleFactoryAdaptor(java.SystemModulesFactory))
-	ctx.RegisterModuleType("prebuilt_apis", android.ModuleFactoryAdaptor(java.PrebuiltApisFactory))
 	ctx.PreArchMutators(android.RegisterPrebuiltsPreArchMutators)
 	ctx.PreArchMutators(android.RegisterPrebuiltsPostDepsMutators)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("prebuilt_apis", java.PrebuiltApisMutator).Parallel()
-	})
 
 	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
 	ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
@@ -91,45 +86,20 @@ func testContext(config android.Config, bp string,
 	bp += cc.GatherRequiredDepsForTest(android.Android)
 
 	mockFS := map[string][]byte{
-		"Android.bp":             []byte(bp),
-		"a.java":                 nil,
-		"b.java":                 nil,
-		"c.java":                 nil,
-		"d.cpp":                  nil,
-		"api/current.txt":        nil,
-		"api/removed.txt":        nil,
-		"api/system-current.txt": nil,
-		"api/system-removed.txt": nil,
-		"api/test-current.txt":   nil,
-		"api/test-removed.txt":   nil,
-		"framework/aidl/a.aidl":  nil,
-
-		"prebuilts/sdk/current/core/android.jar":                              nil,
-		"prebuilts/sdk/current/public/android.jar":                            nil,
-		"prebuilts/sdk/current/public/framework.aidl":                         nil,
-		"prebuilts/sdk/current/public/core.jar":                               nil,
-		"prebuilts/sdk/current/system/android.jar":                            nil,
-		"prebuilts/sdk/current/test/android.jar":                              nil,
-		"prebuilts/sdk/28/public/api/sysprop-platform.txt":                    nil,
-		"prebuilts/sdk/28/system/api/sysprop-platform.txt":                    nil,
-		"prebuilts/sdk/28/test/api/sysprop-platform.txt":                      nil,
-		"prebuilts/sdk/28/public/api/sysprop-platform-removed.txt":            nil,
-		"prebuilts/sdk/28/system/api/sysprop-platform-removed.txt":            nil,
-		"prebuilts/sdk/28/test/api/sysprop-platform-removed.txt":              nil,
-		"prebuilts/sdk/28/public/api/sysprop-platform-on-product.txt":         nil,
-		"prebuilts/sdk/28/system/api/sysprop-platform-on-product.txt":         nil,
-		"prebuilts/sdk/28/test/api/sysprop-platform-on-product.txt":           nil,
-		"prebuilts/sdk/28/public/api/sysprop-platform-on-product-removed.txt": nil,
-		"prebuilts/sdk/28/system/api/sysprop-platform-on-product-removed.txt": nil,
-		"prebuilts/sdk/28/test/api/sysprop-platform-on-product-removed.txt":   nil,
-		"prebuilts/sdk/28/public/api/sysprop-vendor.txt":                      nil,
-		"prebuilts/sdk/28/system/api/sysprop-vendor.txt":                      nil,
-		"prebuilts/sdk/28/test/api/sysprop-vendor.txt":                        nil,
-		"prebuilts/sdk/28/public/api/sysprop-vendor-removed.txt":              nil,
-		"prebuilts/sdk/28/system/api/sysprop-vendor-removed.txt":              nil,
-		"prebuilts/sdk/28/test/api/sysprop-vendor-removed.txt":                nil,
-		"prebuilts/sdk/tools/core-lambda-stubs.jar":                           nil,
-		"prebuilts/sdk/Android.bp":                                            []byte(`prebuilt_apis { name: "sdk", api_dirs: ["28", "current"],}`),
+		"Android.bp":                       []byte(bp),
+		"a.java":                           nil,
+		"b.java":                           nil,
+		"c.java":                           nil,
+		"d.cpp":                            nil,
+		"api/sysprop-platform-current.txt": nil,
+		"api/sysprop-platform-latest.txt":  nil,
+		"api/sysprop-platform-on-product-current.txt": nil,
+		"api/sysprop-platform-on-product-latest.txt":  nil,
+		"api/sysprop-vendor-current.txt":              nil,
+		"api/sysprop-vendor-latest.txt":               nil,
+		"api/sysprop-odm-current.txt":                 nil,
+		"api/sysprop-odm-latest.txt":                  nil,
+		"framework/aidl/a.aidl":                       nil,
 
 		// For framework-res, which is an implicit dependency for framework
 		"AndroidManifest.xml":                        nil,
@@ -155,6 +125,7 @@ func testContext(config android.Config, bp string,
 
 		"android/sysprop/PlatformProperties.sysprop": nil,
 		"com/android/VendorProperties.sysprop":       nil,
+		"com/android2/OdmProperties.sysprop":         nil,
 	}
 
 	for k, v := range fs {
@@ -168,7 +139,7 @@ func testContext(config android.Config, bp string,
 
 func run(t *testing.T, ctx *android.TestContext, config android.Config) {
 	t.Helper()
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
 	android.FailIfErrored(t, errs)
@@ -221,6 +192,14 @@ func TestSyspropLibrary(t *testing.T) {
 			vendor_available: true,
 		}
 
+		sysprop_library {
+			name: "sysprop-odm",
+			srcs: ["com/android2/OdmProperties.sysprop"],
+			api_packages: ["com.android2"],
+			property_owner: "Odm",
+			device_specific: true,
+		}
+
 		java_library {
 			name: "java-platform",
 			srcs: ["c.java"],
@@ -288,21 +267,41 @@ func TestSyspropLibrary(t *testing.T) {
 			name: "liblog",
 			symbol_file: "",
 		}
+
+		java_library {
+			name: "sysprop-library-stub-platform",
+			sdk_version: "core_current",
+		}
+
+		java_library {
+			name: "sysprop-library-stub-vendor",
+			soc_specific: true,
+			sdk_version: "core_current",
+		}
 		`)
 
+	// Check for generated cc_library
 	for _, variant := range []string{
-		"android_arm_armv7-a-neon_core_shared",
-		"android_arm_armv7-a-neon_core_static",
 		"android_arm_armv7-a-neon_vendor_shared",
 		"android_arm_armv7-a-neon_vendor_static",
-		"android_arm64_armv8-a_core_shared",
-		"android_arm64_armv8-a_core_static",
 		"android_arm64_armv8-a_vendor_shared",
 		"android_arm64_armv8-a_vendor_static",
 	} {
-		// Check for generated cc_library
 		ctx.ModuleForTests("libsysprop-platform", variant)
 		ctx.ModuleForTests("libsysprop-vendor", variant)
+		ctx.ModuleForTests("libsysprop-odm", variant)
+	}
+
+	for _, variant := range []string{
+		"android_arm_armv7-a-neon_core_shared",
+		"android_arm_armv7-a-neon_core_static",
+		"android_arm64_armv8-a_core_shared",
+		"android_arm64_armv8-a_core_static",
+	} {
+		ctx.ModuleForTests("libsysprop-platform", variant)
+
+		// core variant of vendor-owned sysprop_library is for product
+		ctx.ModuleForTests("libsysprop-vendor", variant)
 	}
 
 	ctx.ModuleForTests("sysprop-platform", "android_common")