add symlink for compatibility (vndk apex)

With VNDK APEX, the location of VNDK lib is changed.
But vndk libs of older VNDK versions still depend on old location.

For current VNDK version, those hard-coded references will be fixed.
TODO(b/142911355): [VNDK APEX] Fix hard-coded references to /system/lib/vndk

Bug: 143192278
Bug: 142912195
Test: m com.android.vndk.current and check if /system/lib/vndk
      OVERRIDE_TARGET_FLATTEN_APEX m com.android.vndk.current

Change-Id: I2ebacde7fcd1c7621e4509e08a76765e1dfeb059
diff --git a/apex/apex.go b/apex/apex.go
index 51a963b..629cbbf 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -126,11 +126,15 @@
 	}, "image_content_file", "whitelisted_files_file", "apex_module_name")
 )
 
-var imageApexSuffix = ".apex"
-var zipApexSuffix = ".zipapex"
+const (
+	imageApexSuffix = ".apex"
+	zipApexSuffix   = ".zipapex"
 
-var imageApexType = "image"
-var zipApexType = "zip"
+	imageApexType = "image"
+	zipApexType   = "zip"
+
+	vndkApexNamePrefix = "com.android.vndk.v"
+)
 
 type dependencyTag struct {
 	blueprint.BaseDependencyTag
@@ -223,7 +227,7 @@
 
 		vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
 		// Ensure VNDK APEX mount point is formatted as com.android.vndk.v###
-		ab.properties.Apex_name = proptools.StringPtr("com.android.vndk.v" + vndkVersion)
+		ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion)
 
 		// vndk_version should be unique
 		vndkApexListMutex.Lock()
@@ -612,6 +616,12 @@
 
 	// intermediate path for apex_manifest.json
 	manifestOut android.WritablePath
+
+	// list of commands to create symlinks for backward compatibility
+	// these commands will be attached as LOCAL_POST_INSTALL_CMD to
+	// apex package itself(for unflattened build) or apex_manifest.json(for flattened build)
+	// so that compat symlinks are always installed regardless of TARGET_FLATTEN_APEX setting.
+	compatSymlinks []string
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -1294,6 +1304,8 @@
 		a.buildUnflattenedApex(ctx, imageApex)
 		a.buildFlattenedApex(ctx)
 	}
+
+	a.compatSymlinks = makeCompatSymlinks(apexName, ctx)
 }
 
 func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
@@ -1711,6 +1723,10 @@
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
+			// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
+			if !a.isFlattenedVariant() && fi.builtFile.Base() == "apex_manifest.json" && len(a.compatSymlinks) > 0 {
+				fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(a.compatSymlinks, " && "))
+			}
 			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 		}
 	}
@@ -1761,10 +1777,16 @@
 				if len(a.externalDeps) > 0 {
 					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.externalDeps, " "))
 				}
+				var postInstallCommands []string
 				if a.prebuiltFileToDelete != "" {
-					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", "rm -rf "+
+					postInstallCommands = append(postInstallCommands, "rm -rf "+
 						filepath.Join(a.installDir.ToMakePath().String(), a.prebuiltFileToDelete))
 				}
+				// For unflattened apexes, compat symlinks are attached to apex package itself as LOCAL_POST_INSTALL_CMD
+				postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
+				if len(postInstallCommands) > 0 {
+					fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
+				}
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 
 				if apexType == imageApex {
@@ -1995,6 +2017,8 @@
 	if p.installable() {
 		ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
 	}
+
+	// TODO(b/143192278): Add compat symlinks for prebuilt_apex
 }
 
 func (p *Prebuilt) Prebuilt() *android.Prebuilt {
@@ -2029,3 +2053,30 @@
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	return module
 }
+
+func makeCompatSymlinks(apexName string, ctx android.ModuleContext) (symlinks []string) {
+	// small helper to add symlink commands
+	addSymlink := func(target, dir, linkName string) {
+		outDir := filepath.Join("$(PRODUCT_OUT)", dir)
+		link := filepath.Join(outDir, linkName)
+		symlinks = append(symlinks, "mkdir -p "+outDir+" && rm -rf "+link+" && ln -sf "+target+" "+link)
+	}
+
+	// TODO(b/142911355): [VNDK APEX] Fix hard-coded references to /system/lib/vndk
+	// When all hard-coded references are fixed, remove symbolic links
+	// Note that  we should keep following symlinks for older VNDKs (<=29)
+	// Since prebuilt vndk libs still depend on system/lib/vndk path
+	if strings.HasPrefix(apexName, vndkApexNamePrefix) {
+		// the name of vndk apex is formatted "com.android.vndk.v" + version
+		vndkVersion := strings.TrimPrefix(apexName, vndkApexNamePrefix)
+		if ctx.Config().Android64() {
+			addSymlink("/apex/"+apexName+"/lib64", "/system/lib64", "vndk-sp-"+vndkVersion)
+			addSymlink("/apex/"+apexName+"/lib64", "/system/lib64", "vndk-"+vndkVersion)
+		}
+		if !ctx.Config().Android64() || ctx.DeviceConfig().DeviceSecondaryArch() != "" {
+			addSymlink("/apex/"+apexName+"/lib", "/system/lib", "vndk-sp-"+vndkVersion)
+			addSymlink("/apex/"+apexName+"/lib", "/system/lib", "vndk-"+vndkVersion)
+		}
+	}
+	return
+}