Support VNDK extensions

This commit adds `extends: "name"` property and provides basic support
to VNDK extensions.  This is the simplest example:

```
cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
}
```

A vndk extension library must extend an existing vndk library which has
`vendor_available: true`.  These two libraries must have the same
`support_system_process` property.

VNDK-ext libraries are installed to `/vendor/lib[64]/vndk` and
VNDK-SP-ext libraries are installed to `/vendor/lib[64]/vndk-sp` by
default.

If there is a matching abi-dumps in `prebuilts/abi-dumps`,
`header-abi-diff` will be invoked to check for ABI breakages.

Bug: 38340960

Test: lunch aosp_walleye-userdebug && make -j8   # runs unit tests

Test: lunch aosp_arm-userdebug && make -j8  # build a target w/o VNDK

Test: Create a lsdump for a vndk lib, add an exported API to vndk lib,
and build fails as expected.

Test: Create a lsdump for a vndk lib, create an vndk extension lib with
extra API, and build succeeds as expected.

Test: Create libutils_ext, add an extra function to libutils_ext, and
call it from a HIDL service.

Change-Id: Iba90e08848ee99814405457f047321e6b52b2df0
diff --git a/cc/builder.go b/cc/builder.go
index 1e1c4f2..fe35d5c 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -38,7 +38,6 @@
 
 var (
 	abiCheckAllowFlags = []string{
-		"-allow-extensions",
 		"-allow-unreferenced-changes",
 		"-allow-unreferenced-elf-symbol-changes",
 	}
@@ -711,12 +710,18 @@
 }
 
 func SourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
-	baseName, exportedHeaderFlags string) android.OptionalPath {
+	baseName, exportedHeaderFlags string, isVndkExt bool) android.OptionalPath {
+
 	outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
+
 	localAbiCheckAllowFlags := append([]string(nil), abiCheckAllowFlags...)
 	if exportedHeaderFlags == "" {
 		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-advice-only")
 	}
+	if isVndkExt {
+		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-allow-extensions")
+	}
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        sAbiDiff,
 		Description: "header-abi-diff " + outputFile.Base(),
diff --git a/cc/cc.go b/cc/cc.go
index a3af304..9b1f220 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -205,9 +205,11 @@
 	useVndk() bool
 	isVndk() bool
 	isVndkSp() bool
+	isVndkExt() bool
 	createVndkSourceAbiDump() bool
 	selectedStl() string
 	baseModuleName() string
+	getVndkExtendsModuleName() string
 }
 
 type ModuleContext interface {
@@ -289,6 +291,7 @@
 	reuseObjTag           = dependencyTag{name: "reuse objects"}
 	ndkStubDepTag         = dependencyTag{name: "ndk stub", library: true}
 	ndkLateStubDepTag     = dependencyTag{name: "ndk late stub", library: true}
+	vndkExtDepTag         = dependencyTag{name: "vndk extends", library: true}
 )
 
 // Module contains the properties and members used by all C/C++ module types, and implements
@@ -398,12 +401,33 @@
 }
 
 func (c *Module) isVndk() bool {
-	if c.vndkdep != nil {
-		return c.vndkdep.isVndk()
+	if vndkdep := c.vndkdep; vndkdep != nil {
+		return vndkdep.isVndk()
 	}
 	return false
 }
 
+func (c *Module) isVndkSp() bool {
+	if vndkdep := c.vndkdep; vndkdep != nil {
+		return vndkdep.isVndkSp()
+	}
+	return false
+}
+
+func (c *Module) isVndkExt() bool {
+	if vndkdep := c.vndkdep; vndkdep != nil {
+		return vndkdep.isVndkExt()
+	}
+	return false
+}
+
+func (c *Module) getVndkExtendsModuleName() string {
+	if vndkdep := c.vndkdep; vndkdep != nil {
+		return vndkdep.getVndkExtendsModuleName()
+	}
+	return ""
+}
+
 // Returns true only when this module is configured to have core and vendor
 // variants.
 func (c *Module) hasVendorVariant() bool {
@@ -474,18 +498,20 @@
 	return ""
 }
 
-func (ctx *moduleContextImpl) isVndk() bool {
-	return ctx.mod.isVndk()
-}
 func (ctx *moduleContextImpl) useVndk() bool {
 	return ctx.mod.useVndk()
 }
 
+func (ctx *moduleContextImpl) isVndk() bool {
+	return ctx.mod.isVndk()
+}
+
 func (ctx *moduleContextImpl) isVndkSp() bool {
-	if vndk := ctx.mod.vndkdep; vndk != nil {
-		return vndk.isVndkSp()
-	}
-	return false
+	return ctx.mod.isVndkSp()
+}
+
+func (ctx *moduleContextImpl) isVndkExt() bool {
+	return ctx.mod.isVndkExt()
 }
 
 // Create source abi dumps if the module belongs to the list of VndkLibraries.
@@ -504,6 +530,10 @@
 	return ctx.mod.ModuleBase.BaseModuleName()
 }
 
+func (ctx *moduleContextImpl) getVndkExtendsModuleName() string {
+	return ctx.mod.getVndkExtendsModuleName()
+}
+
 func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	return &Module{
 		hod:      hod,
@@ -935,6 +965,18 @@
 		{"ndk_api", version}, {"link", "shared"}}, ndkStubDepTag, variantNdkLibs...)
 	actx.AddVariationDependencies([]blueprint.Variation{
 		{"ndk_api", version}, {"link", "shared"}}, ndkLateStubDepTag, variantLateNdkLibs...)
+
+	if vndkdep := c.vndkdep; vndkdep != nil {
+		if vndkdep.isVndkExt() {
+			baseModuleMode := vendorMode
+			if actx.DeviceConfig().VndkVersion() == "" {
+				baseModuleMode = coreMode
+			}
+			actx.AddVariationDependencies([]blueprint.Variation{
+				{"image", baseModuleMode}, {"link", "shared"}}, vndkExtDepTag,
+				vndkdep.getVndkExtendsModuleName())
+		}
+	}
 }
 
 func beginMutator(ctx android.BottomUpMutatorContext) {
@@ -959,7 +1001,7 @@
 
 // Whether a module can link to another module, taking into
 // account NDK linking.
-func checkLinkType(ctx android.ModuleContext, from *Module, to *Module) {
+func checkLinkType(ctx android.ModuleContext, from *Module, to *Module, tag dependencyTag) {
 	if from.Target().Os != android.Android {
 		// Host code is not restricted
 		return
@@ -969,7 +1011,7 @@
 		// each vendor-available module needs to check
 		// link-type for VNDK.
 		if from.vndkdep != nil {
-			from.vndkdep.vndkCheckLinkType(ctx, to)
+			from.vndkdep.vndkCheckLinkType(ctx, to, tag)
 		}
 		return
 	}
@@ -1151,7 +1193,7 @@
 				}
 			}
 
-			checkLinkType(ctx, c, ccDep)
+			checkLinkType(ctx, c, ccDep, t)
 		}
 
 		var ptr *android.Paths
@@ -1411,21 +1453,47 @@
 	}
 
 	// Sanity check
-	if m.VendorProperties.Vendor_available != nil && (mctx.SocSpecific() || mctx.DeviceSpecific()) {
+	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+
+	if m.VendorProperties.Vendor_available != nil && vendorSpecific {
 		mctx.PropertyErrorf("vendor_available",
 			"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
 		return
 	}
-	if vndk := m.vndkdep; vndk != nil {
-		if vndk.isVndk() && m.VendorProperties.Vendor_available == nil {
-			mctx.PropertyErrorf("vndk",
-				"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
-			return
-		}
-		if !vndk.isVndk() && vndk.isVndkSp() {
-			mctx.PropertyErrorf("vndk",
-				"must set `enabled: true` to set `support_system_process: true`")
-			return
+
+	if vndkdep := m.vndkdep; vndkdep != nil {
+		if vndkdep.isVndk() {
+			if vendorSpecific {
+				if !vndkdep.isVndkExt() {
+					mctx.PropertyErrorf("vndk",
+						"must set `extends: \"...\"` to vndk extension")
+					return
+				}
+			} else {
+				if vndkdep.isVndkExt() {
+					mctx.PropertyErrorf("vndk",
+						"must set `vendor: true` to set `extends: %q`",
+						m.getVndkExtendsModuleName())
+					return
+				}
+				if m.VendorProperties.Vendor_available == nil {
+					mctx.PropertyErrorf("vndk",
+						"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
+					return
+				}
+			}
+		} else {
+			if vndkdep.isVndkSp() {
+				mctx.PropertyErrorf("vndk",
+					"must set `enabled: true` to set `support_system_process: true`")
+				return
+			}
+			if vndkdep.isVndkExt() {
+				mctx.PropertyErrorf("vndk",
+					"must set `enabled: true` to set `extends: %q`",
+					m.getVndkExtendsModuleName())
+				return
+			}
 		}
 	}
 
@@ -1453,14 +1521,14 @@
 			vendor.Properties.PreventInstall = true
 			vendor.Properties.HideFromMake = true
 		}
-	} else if m.hasVendorVariant() {
+	} else if m.hasVendorVariant() && !vendorSpecific {
 		// This will be available in both /system and /vendor
 		// or a /system directory that is available to vendor.
 		mod := mctx.CreateVariations(coreMode, vendorMode)
 		vendor := mod[1].(*Module)
 		vendor.Properties.UseVndk = true
 		squashVendorSrcs(vendor)
-	} else if (mctx.SocSpecific() || mctx.DeviceSpecific()) && String(m.Properties.Sdk_version) == "" {
+	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
 		// This will be available in /vendor (or /odm) only
 		mod := mctx.CreateVariations(vendorMode)
 		vendor := mod[0].(*Module)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 4d8c4fb..19e4703 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -22,6 +22,7 @@
 	"io/ioutil"
 	"os"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 	"testing"
@@ -52,10 +53,7 @@
 	os.Exit(run())
 }
 
-func testCc(t *testing.T, bp string) *android.TestContext {
-	config := android.TestArchConfig(buildDir, nil)
-	config.ProductVariables.DeviceVndkVersion = StringPtr("current")
-
+func createTestContext(t *testing.T, config android.Config, bp string) *android.TestContext {
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(LibraryFactory))
 	ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(LibrarySharedFactory))
@@ -90,8 +88,8 @@
 
 		cc_library {
 			name: "libc",
-			no_libgcc : true,
-			nocrt : true,
+			no_libgcc: true,
+			nocrt: true,
 			system_shared_libs: [],
 		}
 		llndk_library {
@@ -100,8 +98,8 @@
 		}
 		cc_library {
 			name: "libm",
-			no_libgcc : true,
-			nocrt : true,
+			no_libgcc: true,
+			nocrt: true,
 			system_shared_libs: [],
 		}
 		llndk_library {
@@ -110,8 +108,8 @@
 		}
 		cc_library {
 			name: "libdl",
-			no_libgcc : true,
-			nocrt : true,
+			no_libgcc: true,
+			nocrt: true,
 			system_shared_libs: [],
 		}
 		llndk_library {
@@ -142,6 +140,12 @@
 		"my_include": nil,
 	})
 
+	return ctx
+}
+
+func testCcWithConfig(t *testing.T, bp string, config android.Config) *android.TestContext {
+	ctx := createTestContext(t, config, bp)
+
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	failIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
@@ -150,14 +154,56 @@
 	return ctx
 }
 
+func testCc(t *testing.T, bp string) *android.TestContext {
+	config := android.TestArchConfig(buildDir, nil)
+	config.ProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.ProductVariables.Platform_vndk_version = StringPtr("VER")
+
+	return testCcWithConfig(t, bp, config)
+}
+
+func testCcNoVndk(t *testing.T, bp string) *android.TestContext {
+	config := android.TestArchConfig(buildDir, nil)
+	config.ProductVariables.Platform_vndk_version = StringPtr("VER")
+
+	return testCcWithConfig(t, bp, config)
+}
+
+func testCcError(t *testing.T, pattern string, bp string) {
+	config := android.TestArchConfig(buildDir, nil)
+	config.ProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.ProductVariables.Platform_vndk_version = StringPtr("VER")
+
+	ctx := createTestContext(t, config, bp)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	if len(errs) > 0 {
+		failIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+
+	_, errs = ctx.PrepareBuildActions(config)
+	if len(errs) > 0 {
+		failIfNoMatchingErrors(t, pattern, errs)
+		return
+	}
+
+	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
+
+const (
+	coreVariant   = "android_arm64_armv8-a_core_shared"
+	vendorVariant = "android_arm64_armv8-a_vendor_shared"
+)
+
 func TestVendorSrc(t *testing.T) {
 	ctx := testCc(t, `
 		cc_library {
 			name: "libTest",
 			srcs: ["foo.c"],
-			no_libgcc : true,
-			nocrt : true,
-			system_shared_libs : [],
+			no_libgcc: true,
+			nocrt: true,
+			system_shared_libs: [],
 			vendor_available: true,
 			target: {
 				vendor: {
@@ -167,7 +213,7 @@
 		}
 	`)
 
-	ld := ctx.ModuleForTests("libTest", "android_arm_armv7-a-neon_vendor_shared").Rule("ld")
+	ld := ctx.ModuleForTests("libTest", vendorVariant).Rule("ld")
 	var objs []string
 	for _, o := range ld.Inputs {
 		objs = append(objs, o.Base())
@@ -177,6 +223,524 @@
 	}
 }
 
+func checkVndkModule(t *testing.T, ctx *android.TestContext, name, subDir string,
+	isVndkSp bool, extends string) {
+
+	mod := ctx.ModuleForTests(name, vendorVariant).Module().(*Module)
+	if !mod.hasVendorVariant() {
+		t.Error("%q must have vendor variant", name)
+	}
+
+	// Check library properties.
+	lib, ok := mod.compiler.(*libraryDecorator)
+	if !ok {
+		t.Errorf("%q must have libraryDecorator", name)
+	} else if lib.baseInstaller.subDir != subDir {
+		t.Errorf("%q must use %q as subdir but it is using %q", name, subDir,
+			lib.baseInstaller.subDir)
+	}
+
+	// Check VNDK properties.
+	if mod.vndkdep == nil {
+		t.Fatalf("%q must have `vndkdep`", name)
+	}
+	if !mod.isVndk() {
+		t.Errorf("%q isVndk() must equal to true", name)
+	}
+	if mod.isVndkSp() != isVndkSp {
+		t.Errorf("%q isVndkSp() must equal to %t", name, isVndkSp)
+	}
+
+	// Check VNDK extension properties.
+	isVndkExt := extends != ""
+	if mod.isVndkExt() != isVndkExt {
+		t.Errorf("%q isVndkExt() must equal to %t", name, isVndkExt)
+	}
+
+	if actualExtends := mod.getVndkExtendsModuleName(); actualExtends != extends {
+		t.Errorf("%q must extend from %q but get %q", name, extends, actualExtends)
+	}
+}
+
+func TestVndk(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_private",
+			vendor_available: false,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp_private",
+			vendor_available: false,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+	`)
+
+	checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "")
+	checkVndkModule(t, ctx, "libvndk_private", "vndk-VER", false, "")
+	checkVndkModule(t, ctx, "libvndk_sp", "vndk-sp-VER", true, "")
+	checkVndkModule(t, ctx, "libvndk_sp_private", "vndk-sp-VER", true, "")
+}
+
+func TestVndkExt(t *testing.T) {
+	// This test checks the VNDK-Ext properties.
+	ctx := testCc(t, `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+	`)
+
+	checkVndkModule(t, ctx, "libvndk_ext", "vndk", false, "libvndk")
+}
+
+func TestVndkExtNoVndk(t *testing.T) {
+	// This test checks the VNDK-Ext properties when BOARD_VNDK_VERSION is not set.
+	ctx := testCcNoVndk(t, `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+	`)
+
+	// Ensures that the core variant of "libvndk_ext" can be found.
+	mod := ctx.ModuleForTests("libvndk_ext", coreVariant).Module().(*Module)
+	if extends := mod.getVndkExtendsModuleName(); extends != "libvndk" {
+		t.Errorf("\"libvndk_ext\" must extend from \"libvndk\" but get %q", extends)
+	}
+}
+
+func TestVndkExtError(t *testing.T) {
+	// This test ensures an error is emitted in ill-formed vndk-ext definition.
+	testCcError(t, "must set `vendor: true` to set `extends: \".*\"`", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+	`)
+
+	testCcError(t, "must set `extends: \"\\.\\.\\.\"` to vndk extension", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+	`)
+}
+
+func TestVndkExtInconsistentSupportSystemProcessError(t *testing.T) {
+	// This test ensures an error is emitted for inconsistent support_system_process.
+	testCcError(t, "module \".*\" with mismatched support_system_process", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+	`)
+
+	testCcError(t, "module \".*\" with mismatched support_system_process", `
+		cc_library {
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk_sp",
+			},
+			nocrt: true,
+		}
+	`)
+}
+
+func TestVndkExtVendorAvailableFalseError(t *testing.T) {
+	// This test ensures an error is emitted when a vndk-ext library extends a vndk library
+	// with `vendor_available: false`.
+	testCcError(t, "`extends` refers module \".*\" which does not have `vendor_available: true`", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: false,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+	`)
+}
+
+func TestVendorModuleUsesVndkExt(t *testing.T) {
+	// This test ensures a vendor module can depend on a vndk-ext library.
+	testCc(t, `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk_sp",
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvendor",
+			vendor: true,
+			shared_libs: ["libvndk_ext", "libvndk_sp_ext"],
+			nocrt: true,
+		}
+	`)
+}
+
+func TestVndkExtUsesVendorLib(t *testing.T) {
+	// This test ensures a vndk-ext library can depend on a vendor library.
+	testCc(t, `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			shared_libs: ["libvendor"],
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvendor",
+			vendor: true,
+			nocrt: true,
+		}
+	`)
+}
+
+func TestVndkSpExtUsesVendorLibError(t *testing.T) {
+	// This test ensures an error is emitted if a vndk-sp-ext library depends on a vendor
+	// library.
+	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
+		cc_library {
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk_sp",
+				support_system_process: true,
+			},
+			shared_libs: ["libvendor"],  // Cause an error
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvendor",
+			vendor: true,
+			nocrt: true,
+		}
+	`)
+}
+
+func TestVndkUsesVndkExtError(t *testing.T) {
+	// This test ensures an error is emitted if a vndk/vndk-sp library depends on a
+	// vndk-ext/vndk-sp-ext library.
+	testCcError(t, "dependency \".*\" of \".*\" missing variant", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk2",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			shared_libs: ["libvndk_ext"],
+			nocrt: true,
+		}
+	`)
+
+	// The pattern should be "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\""
+	// but target.vendor.shared_libs has not been supported yet.
+	testCcError(t, "unrecognized property \"target.vendor.shared_libs\"", `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk",
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk2",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			target: {
+				vendor: {
+					shared_libs: ["libvndk_ext"],
+				},
+			},
+			nocrt: true,
+		}
+	`)
+
+	testCcError(t, "dependency \".*\" of \".*\" missing variant", `
+		cc_library {
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk_sp",
+				support_system_process: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp_2",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			shared_libs: ["libvndk_sp_ext"],
+			nocrt: true,
+		}
+	`)
+
+	// The pattern should be "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\""
+	// but target.vendor.shared_libs has not been supported yet.
+	testCcError(t, "unrecognized property \"target.vendor.shared_libs\"", `
+		cc_library {
+			name: "libvndk_sp",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp_ext",
+			vendor: true,
+			vndk: {
+				enabled: true,
+				extends: "libvndk_sp",
+			},
+			nocrt: true,
+		}
+
+		cc_library {
+			name: "libvndk_sp2",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			target: {
+				vendor: {
+					shared_libs: ["libvndk_sp_ext"],
+				},
+			},
+			nocrt: true,
+		}
+	`)
+}
+
 var (
 	str11 = "01234567891"
 	str10 = str11[:10]
@@ -499,6 +1063,7 @@
 		}
 	}
 }
+
 func failIfErrored(t *testing.T, errs []error) {
 	if len(errs) > 0 {
 		for _, err := range errs {
@@ -508,6 +1073,29 @@
 	}
 }
 
+func failIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
+	matcher, err := regexp.Compile(pattern)
+	if err != nil {
+		t.Errorf("failed to compile regular expression %q because %s", pattern, err)
+	}
+
+	found := false
+
+	for _, err := range errs {
+		if matcher.FindStringIndex(err.Error()) != nil {
+			found = true
+			break
+		}
+	}
+
+	if !found {
+		t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
+		for i, err := range errs {
+			t.Errorf("errs[%d] = %s", i, err)
+		}
+	}
+}
+
 func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
 	for _, moduleName := range moduleNames {
 		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
@@ -597,8 +1185,8 @@
 		shared_libs: ["libllndk"],
 		vendor: true,
 		srcs: ["foo.c"],
-		no_libgcc : true,
-		nocrt : true,
+		no_libgcc: true,
+		nocrt: true,
 	}
 	`)
 
diff --git a/cc/library.go b/cc/library.go
index 9661f44..00282fc 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -416,6 +416,10 @@
 		name = ctx.baseModuleName()
 	}
 
+	if ctx.isVndkExt() {
+		name = ctx.getVndkExtendsModuleName()
+	}
+
 	if ctx.Host() && Bool(library.Properties.Unique_host_soname) {
 		if !strings.HasSuffix(name, "-host") {
 			name = name + "-host"
@@ -619,7 +623,12 @@
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
 	//Also take into account object re-use.
 	if len(objs.sAbiDumpFiles) > 0 && ctx.createVndkSourceAbiDump() {
-		refSourceDumpFile := android.PathForVndkRefAbiDump(ctx, "current", fileName, vndkVsNdk(ctx), true)
+		vndkVersion := "current"
+		if ver := ctx.DeviceConfig().VndkVersion(); ver != "" {
+			vndkVersion = ver
+		}
+
+		refSourceDumpFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, vndkVsNdk(ctx), true)
 		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
 		var SourceAbiFlags []string
 		for _, dir := range exportIncludeDirs.Strings() {
@@ -632,7 +641,8 @@
 		library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags)
 		if refSourceDumpFile.Valid() {
 			unzippedRefDump := UnzipRefDump(ctx, refSourceDumpFile.Path(), fileName)
-			library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(), unzippedRefDump, fileName, exportedHeaderFlags)
+			library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
+				unzippedRefDump, fileName, exportedHeaderFlags, ctx.isVndkExt())
 		}
 	}
 }
@@ -721,8 +731,13 @@
 			} else if ctx.isVndk() {
 				library.baseInstaller.subDir = "vndk"
 			}
-			if ctx.isVndk() && ctx.DeviceConfig().PlatformVndkVersion() != "current" {
-				library.baseInstaller.subDir += "-" + ctx.DeviceConfig().PlatformVndkVersion()
+
+			// Append a version to vndk or vndk-sp directories on the system partition.
+			if ctx.isVndk() && !ctx.isVndkExt() {
+				vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
+				if vndkVersion != "current" && vndkVersion != "" {
+					library.baseInstaller.subDir += "-" + vndkVersion
+				}
 			}
 		}
 		library.baseInstaller.install(ctx, file)
diff --git a/cc/vndk.go b/cc/vndk.go
index a61b74c..d417bea 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -42,6 +42,9 @@
 		// the module is VNDK-core and can link to other VNDK-core,
 		// VNDK-SP or LL-NDK modules only.
 		Support_system_process *bool
+
+		// Extending another module
+		Extends *string
 	}
 }
 
@@ -67,17 +70,31 @@
 	return Bool(vndk.Properties.Vndk.Support_system_process)
 }
 
+func (vndk *vndkdep) isVndkExt() bool {
+	return vndk.Properties.Vndk.Extends != nil
+}
+
+func (vndk *vndkdep) getVndkExtendsModuleName() string {
+	return String(vndk.Properties.Vndk.Extends)
+}
+
 func (vndk *vndkdep) typeName() string {
 	if !vndk.isVndk() {
 		return "native:vendor"
 	}
-	if !vndk.isVndkSp() {
-		return "native:vendor:vndk"
+	if !vndk.isVndkExt() {
+		if !vndk.isVndkSp() {
+			return "native:vendor:vndk"
+		}
+		return "native:vendor:vndksp"
 	}
-	return "native:vendor:vndksp"
+	if !vndk.isVndkSp() {
+		return "native:vendor:vndkext"
+	}
+	return "native:vendor:vndkspext"
 }
 
-func (vndk *vndkdep) vndkCheckLinkType(ctx android.ModuleContext, to *Module) {
+func (vndk *vndkdep) vndkCheckLinkType(ctx android.ModuleContext, to *Module, tag dependencyTag) {
 	if to.linker == nil {
 		return
 	}
@@ -109,11 +126,43 @@
 			vndk.typeName(), to.Name())
 		return
 	}
+	if tag == vndkExtDepTag {
+		// Ensure `extends: "name"` property refers a vndk module that has vendor_available
+		// and has identical vndk properties.
+		if to.vndkdep == nil || !to.vndkdep.isVndk() {
+			ctx.ModuleErrorf("`extends` refers a non-vndk module %q", to.Name())
+			return
+		}
+		if vndk.isVndkSp() != to.vndkdep.isVndkSp() {
+			ctx.ModuleErrorf(
+				"`extends` refers a module %q with mismatched support_system_process",
+				to.Name())
+			return
+		}
+		if !Bool(to.VendorProperties.Vendor_available) {
+			ctx.ModuleErrorf(
+				"`extends` refers module %q which does not have `vendor_available: true`",
+				to.Name())
+			return
+		}
+	}
 	if to.vndkdep == nil {
 		return
 	}
-	if (vndk.isVndk() && !to.vndkdep.isVndk()) || (vndk.isVndkSp() && !to.vndkdep.isVndkSp()) {
-		ctx.ModuleErrorf("(%s) should not link to %q(%s)",
+
+	// VNDK-core and VNDK-SP must not depend on VNDK extensions.
+	if (vndk.isVndk() || vndk.isVndkSp()) && !vndk.isVndkExt() && to.vndkdep.isVndkExt() {
+		ctx.ModuleErrorf("(%s) should not link to %q (%s)",
+			vndk.typeName(), to.Name(), to.vndkdep.typeName())
+		return
+	}
+
+	// VNDK-core must be only depend on VNDK-SP or LL-NDK. VNDK-SP must only depend on
+	// LL-NDK, regardless the extension status. VNDK-Ext may depend on vendor libraries, but
+	// VNDK-SP-Ext must remain self-contained.
+	if (vndk.isVndk() && !to.vndkdep.isVndk() && !vndk.isVndkExt()) ||
+		(vndk.isVndkSp() && !to.vndkdep.isVndkSp()) {
+		ctx.ModuleErrorf("(%s) should not link to %q (%s)",
 			vndk.typeName(), to.Name(), to.vndkdep.typeName())
 		return
 	}
@@ -149,7 +198,7 @@
 			prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker)
 			if (is_lib && lib.shared()) || (is_prebuilt_lib && prebuilt_lib.shared()) {
 				name := strings.TrimPrefix(m.Name(), "prebuilt_")
-				if m.vndkdep.isVndk() {
+				if m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
 					vndkLibrariesLock.Lock()
 					defer vndkLibrariesLock.Unlock()
 					if m.vndkdep.isVndkSp() {