Make soong-only builds adb-sync compatible

Historically, the build has put the staging directories for partitions
in out/target/product. But in soong-only builds, the main staging
directory is in the out/soong/.intermediates path for the filesystem
modules, and then later also replicated in out/target/product for
backwards compatibility with tools like adb sync.

However, adb sync determines which files to push by if they have
a different timestamp than on device. Since the device images were
built using a different staging directory than the one adb sync is
looking at, the have different timestamps, and adb sync syncs all files
on the device the first time it's run.

To work around this, ensure the files in both staging directories
have the same timestamp by preserving the timestamp from their
source file with the `cp -p` flag.

This cl only does that change for real files right now, not symlinks.
We could also do it for symlinks in the future, but they're more
complicated and I didn't want to deal with that right now. This means
the first adb sync will resync all symlinks on the device, but there
are relatively few of them and they're small so it's not that big of
an issue.

Long term, we should rewrite adb sync to not depend on staging
directories. If it looks at the actual module intermediates files,
it will see a more consistent timestamp.

Bug: 396466053
Test: Manually ran m sync workflows. Also testing togging soong-only on and off and rebuilding
Change-Id: Ia0076a2f8e58404c5c3a0bf0d33b09b6846e95bd
diff --git a/android/module_context.go b/android/module_context.go
index 45a8e5c..706ead7 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -713,6 +713,17 @@
 				implicitDeps = append(implicitDeps, extraZip.zip)
 			}
 
+			var cpFlags = "-f"
+
+			// If this is a device file, copy while preserving timestamps. This is to support
+			// adb sync in soong-only builds. Because soong-only builds have 2 different staging
+			// directories, the out/target/product one and the out/soong/.intermediates one,
+			// we need to ensure the files in them have the same timestamps so that adb sync doesn't
+			// update the files on device.
+			if strings.Contains(fullInstallPath.String(), "target/product") {
+				cpFlags += " -p"
+			}
+
 			m.Build(pctx, BuildParams{
 				Rule:        rule,
 				Description: "install " + fullInstallPath.Base(),
@@ -722,7 +733,7 @@
 				OrderOnly:   orderOnlyDeps,
 				Args: map[string]string{
 					"extraCmds": extraCmds,
-					"cpFlags":   "-f",
+					"cpFlags":   cpFlags,
 				},
 			})
 		}
diff --git a/android/packaging.go b/android/packaging.go
index d216c0c..551fd4c 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -598,12 +598,12 @@
 func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
 	dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec)
 	dirsToSpecs[dir] = specs
-	return p.CopySpecsToDirs(ctx, builder, dirsToSpecs)
+	return p.CopySpecsToDirs(ctx, builder, dirsToSpecs, false)
 }
 
 // CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec
 // entries into corresponding directories.
-func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) {
+func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec, preserveTimestamps bool) (entries []string) {
 	empty := true
 	for _, specs := range dirsToSpecs {
 		if len(specs) > 0 {
@@ -637,7 +637,11 @@
 				builder.Command().Textf("mkdir -p %s", destDir)
 			}
 			if ps.symlinkTarget == "" {
-				builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
+				cmd := builder.Command().Text("cp")
+				if preserveTimestamps {
+					cmd.Flag("-p")
+				}
+				cmd.Input(ps.srcPath).Text(destPath)
 			} else {
 				builder.Command().Textf("ln -sf %s %s", ps.symlinkTarget, destPath)
 			}