Merge "Prebuilt stub not available to platform is handled correctly"
diff --git a/android/apex.go b/android/apex.go
index 4a71b40..f6eca86 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -153,13 +153,12 @@
 	// run.
 	DirectlyInAnyApex() bool
 
-	// Returns true in the primary variant of a module if _any_ variant of the module is
-	// directly in any apex. This includes host, arch, asan, etc. variants. It is unused in any
-	// variant that is not the primary variant. Ideally this wouldn't be used, as it incorrectly
-	// mixes arch variants if only one arch is in an apex, but a few places depend on it, for
-	// example when an ASAN variant is created before the apexMutator. Call this after
-	// apex.apexMutator is run.
-	AnyVariantDirectlyInAnyApex() bool
+	// NotInPlatform tells whether or not this module is included in an APEX and therefore
+	// shouldn't be exposed to the platform (i.e. outside of the APEX) directly. A module is
+	// considered to be included in an APEX either when there actually is an APEX that
+	// explicitly has the module as its dependency or the module is not available to the
+	// platform, which indicates that the module belongs to at least one or more other APEXes.
+	NotInPlatform() bool
 
 	// Tests if this module could have APEX variants. Even when a module type implements
 	// ApexModule interface, APEX variants are created only for the module instances that return
@@ -221,7 +220,12 @@
 	// See ApexModule.DirectlyInAnyApex()
 	DirectlyInAnyApex bool `blueprint:"mutated"`
 
-	// See ApexModule.AnyVariantDirectlyInAnyApex()
+	// AnyVariantDirectlyInAnyApex is true in the primary variant of a module if _any_ variant
+	// of the module is directly in any apex. This includes host, arch, asan, etc. variants. It
+	// is unused in any variant that is not the primary variant. Ideally this wouldn't be used,
+	// as it incorrectly mixes arch variants if only one arch is in an apex, but a few places
+	// depend on it, for example when an ASAN variant is created before the apexMutator. Call
+	// this after apex.apexMutator is run.
 	AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"`
 
 	// See ApexModule.NotAvailableForPlatform()
@@ -302,8 +306,8 @@
 }
 
 // Implements ApexModule
-func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool {
-	return m.ApexProperties.AnyVariantDirectlyInAnyApex
+func (m *ApexModuleBase) NotInPlatform() bool {
+	return m.ApexProperties.AnyVariantDirectlyInAnyApex || !m.AvailableFor(AvailableToPlatform)
 }
 
 // Implements ApexModule
diff --git a/apex/apex_test.go b/apex/apex_test.go
index ab8ae16..f71e7ef 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -15,6 +15,7 @@
 package apex
 
 import (
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path"
@@ -6411,6 +6412,160 @@
 	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
 }
 
+func TestPrebuiltStubLibDep(t *testing.T) {
+	bpBase := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			apex_available: ["myapex"],
+			shared_libs: ["stublib"],
+			system_shared_libs: [],
+		}
+		apex {
+			name: "otherapex",
+			enabled: %s,
+			key: "myapex.key",
+			native_shared_libs: ["stublib"],
+		}
+	`
+
+	stublibSourceBp := `
+		cc_library {
+			name: "stublib",
+			srcs: ["mylib.cpp"],
+			apex_available: ["otherapex"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["1"],
+			},
+		}
+	`
+
+	stublibPrebuiltBp := `
+		cc_prebuilt_library_shared {
+			name: "stublib",
+			srcs: ["prebuilt.so"],
+			apex_available: ["otherapex"],
+			stubs: {
+				versions: ["1"],
+			},
+			%s
+		}
+	`
+
+	tests := []struct {
+		name             string
+		stublibBp        string
+		usePrebuilt      bool
+		modNames         []string // Modules to collect AndroidMkEntries for
+		otherApexEnabled []string
+	}{
+		{
+			name:             "only_source",
+			stublibBp:        stublibSourceBp,
+			usePrebuilt:      false,
+			modNames:         []string{"stublib"},
+			otherApexEnabled: []string{"true", "false"},
+		},
+		{
+			name:             "source_preferred",
+			stublibBp:        stublibSourceBp + fmt.Sprintf(stublibPrebuiltBp, ""),
+			usePrebuilt:      false,
+			modNames:         []string{"stublib", "prebuilt_stublib"},
+			otherApexEnabled: []string{"true", "false"},
+		},
+		{
+			name:             "prebuilt_preferred",
+			stublibBp:        stublibSourceBp + fmt.Sprintf(stublibPrebuiltBp, "prefer: true,"),
+			usePrebuilt:      true,
+			modNames:         []string{"stublib", "prebuilt_stublib"},
+			otherApexEnabled: []string{"false"}, // No "true" since APEX cannot depend on prebuilt.
+		},
+		{
+			name:             "only_prebuilt",
+			stublibBp:        fmt.Sprintf(stublibPrebuiltBp, ""),
+			usePrebuilt:      true,
+			modNames:         []string{"stublib"},
+			otherApexEnabled: []string{"false"}, // No "true" since APEX cannot depend on prebuilt.
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			for _, otherApexEnabled := range test.otherApexEnabled {
+				t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) {
+					ctx, config := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
+
+					type modAndMkEntries struct {
+						mod       *cc.Module
+						mkEntries android.AndroidMkEntries
+					}
+					entries := []*modAndMkEntries{}
+
+					// Gather shared lib modules that are installable
+					for _, modName := range test.modNames {
+						for _, variant := range ctx.ModuleVariantsForTests(modName) {
+							if !strings.HasPrefix(variant, "android_arm64_armv8-a_shared") {
+								continue
+							}
+							mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module)
+							if !mod.Enabled() || mod.IsSkipInstall() {
+								continue
+							}
+							for _, ent := range android.AndroidMkEntriesForTest(t, config, "", mod) {
+								if ent.Disabled {
+									continue
+								}
+								entries = append(entries, &modAndMkEntries{
+									mod:       mod,
+									mkEntries: ent,
+								})
+							}
+						}
+					}
+
+					var entry *modAndMkEntries = nil
+					for _, ent := range entries {
+						if strings.Join(ent.mkEntries.EntryMap["LOCAL_MODULE"], ",") == "stublib" {
+							if entry != nil {
+								t.Errorf("More than one AndroidMk entry for \"stublib\": %s and %s", entry.mod, ent.mod)
+							} else {
+								entry = ent
+							}
+						}
+					}
+
+					if entry == nil {
+						t.Errorf("AndroidMk entry for \"stublib\" missing")
+					} else {
+						isPrebuilt := entry.mod.Prebuilt() != nil
+						if isPrebuilt != test.usePrebuilt {
+							t.Errorf("Wrong module for \"stublib\" AndroidMk entry: got prebuilt %t, want prebuilt %t", isPrebuilt, test.usePrebuilt)
+						}
+						if !entry.mod.IsStubs() {
+							t.Errorf("Module for \"stublib\" AndroidMk entry isn't a stub: %s", entry.mod)
+						}
+						if entry.mkEntries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"] != nil {
+							t.Errorf("AndroidMk entry for \"stublib\" has LOCAL_NOT_AVAILABLE_FOR_PLATFORM set: %+v", entry.mkEntries)
+						}
+					}
+				})
+			}
+		})
+	}
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 4f4b047..187a2ff 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -46,7 +46,7 @@
 	InRamdisk() bool
 	InVendorRamdisk() bool
 	InRecovery() bool
-	AnyVariantDirectlyInAnyApex() bool
+	NotInPlatform() bool
 }
 
 type subAndroidMkProvider interface {
@@ -281,10 +281,15 @@
 			}
 		})
 	}
-	if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.AnyVariantDirectlyInAnyApex() &&
+	// If a library providing a stub is included in an APEX, the private APIs of the library
+	// is accessible only inside the APEX. From outside of the APEX, clients can only use the
+	// public APIs via the stub. To enforce this, the (latest version of the) stub gets the
+	// name of the library. The impl library instead gets the `.bootstrap` suffix to so that
+	// they can be exceptionally used directly when APEXes are not available (e.g. during the
+	// very early stage in the boot process).
+	if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.NotInPlatform() &&
 		!ctx.InRamdisk() && !ctx.InVendorRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() && !ctx.static() {
 		if library.buildStubs() && library.isLatestStubVersion() {
-			// reference the latest version via its name without suffix when it is provided by apex
 			entries.SubName = ""
 		}
 		if !library.buildStubs() {
diff --git a/cc/cc.go b/cc/cc.go
index d1f5c47..afdf85b 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1586,13 +1586,14 @@
 		}
 		c.outputFile = android.OptionalPathForPath(outputFile)
 
-		// If a lib is directly included in any of the APEXes, unhide the stubs
-		// variant having the latest version gets visible to make. In addition,
-		// the non-stubs variant is renamed to <libname>.bootstrap. This is to
-		// force anything in the make world to link against the stubs library.
-		// (unless it is explicitly referenced via .bootstrap suffix or the
-		// module is marked with 'bootstrap: true').
-		if c.HasStubsVariants() && c.AnyVariantDirectlyInAnyApex() && !c.InRamdisk() &&
+		// If a lib is directly included in any of the APEXes or is not available to the
+		// platform (which is often the case when the stub is provided as a prebuilt),
+		// unhide the stubs variant having the latest version gets visible to make. In
+		// addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to
+		// force anything in the make world to link against the stubs library.  (unless it
+		// is explicitly referenced via .bootstrap suffix or the module is marked with
+		// 'bootstrap: true').
+		if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
 			!c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
 			c.IsStubs() && !c.InVendorRamdisk() {
 			c.Properties.HideFromMake = false // unhide
@@ -2472,7 +2473,7 @@
 						// an APEX (and not from platform)
 						// However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules,
 						// always link to non-stub variant
-						useStubs = dep.(android.ApexModule).AnyVariantDirectlyInAnyApex() && !c.bootstrap()
+						useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap()
 						// Another exception: if this module is bundled with an APEX, then
 						// it is linked with the non-stub variant of a module in the APEX
 						// as if this is part of the APEX.