[incfs] Small cleanup in staging -> final dir rename

Bug: 153704006
Test: atest PackageManagerShellCommandTest \
  PackageManagerShellCommandIncrementalTest \
  IncrementalServiceTest
Change-Id: I227d8e0cba6554bf8cf58a16598f34ccbf3d21c0
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 35518db..916edfa 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -32,8 +32,12 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
 
 /**
  * Provides operations to open or create an IncrementalStorage, using IIncrementalService
@@ -176,25 +180,6 @@
     }
 
     /**
-     * Iterates through path parents to find the base dir of an Incremental Storage.
-     *
-     * @param file Target file to search storage for.
-     * @return Absolute path which is a bind-mount point of Incremental File System.
-     */
-    @Nullable
-    private Path getStoragePathForFile(File file) {
-        File currentPath = new File(file.getParent());
-        while (currentPath.getParent() != null) {
-            IncrementalStorage storage = openStorage(currentPath.getAbsolutePath());
-            if (storage != null) {
-                return currentPath.toPath();
-            }
-            currentPath = new File(currentPath.getParent());
-        }
-        return null;
-    }
-
-    /**
      * Set up an app's code path. The expected outcome of this method is:
      * 1) The actual apk directory under /data/incremental is bind-mounted to the parent directory
      * of {@code afterCodeFile}.
@@ -212,29 +197,27 @@
      */
     public void renameCodePath(File beforeCodeFile, File afterCodeFile)
             throws IllegalArgumentException, IOException {
-        final String beforeCodePath = beforeCodeFile.getAbsolutePath();
-        final String afterCodePathParent = afterCodeFile.getParentFile().getAbsolutePath();
-        if (!isIncrementalPath(beforeCodePath)) {
-            throw new IllegalArgumentException("Not an Incremental path: " + beforeCodePath);
-        }
-        final String afterCodePathName = afterCodeFile.getName();
-        final Path apkStoragePath = Paths.get(beforeCodePath);
-        if (apkStoragePath == null || apkStoragePath.toAbsolutePath() == null) {
-            throw new IOException("Invalid source storage path for: " + beforeCodePath);
-        }
-        final IncrementalStorage apkStorage =
-                openStorage(apkStoragePath.toAbsolutePath().toString());
+        final File beforeCodeAbsolute = beforeCodeFile.getAbsoluteFile();
+        final IncrementalStorage apkStorage = openStorage(beforeCodeAbsolute.toString());
         if (apkStorage == null) {
-            throw new IOException("Failed to retrieve storage from Incremental Service.");
+            throw new IllegalArgumentException("Not an Incremental path: " + beforeCodeAbsolute);
         }
-        final IncrementalStorage linkedApkStorage = createStorage(afterCodePathParent, apkStorage,
-                IncrementalManager.CREATE_MODE_CREATE
-                        | IncrementalManager.CREATE_MODE_PERMANENT_BIND);
+        final String targetStorageDir = afterCodeFile.getAbsoluteFile().getParent();
+        final IncrementalStorage linkedApkStorage =
+                createStorage(targetStorageDir, apkStorage,
+                        IncrementalManager.CREATE_MODE_CREATE
+                                | IncrementalManager.CREATE_MODE_PERMANENT_BIND);
         if (linkedApkStorage == null) {
-            throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent);
+            throw new IOException("Failed to create linked storage at dir: " + targetStorageDir);
         }
-        linkFiles(apkStorage, beforeCodeFile, "", linkedApkStorage, afterCodePathName);
-        apkStorage.unBind(beforeCodePath);
+        try {
+            final String afterCodePathName = afterCodeFile.getName();
+            linkFiles(apkStorage, beforeCodeAbsolute, "", linkedApkStorage, afterCodePathName);
+            apkStorage.unBind(beforeCodeAbsolute.toString());
+        } catch (Exception e) {
+            linkedApkStorage.unBind(targetStorageDir);
+            throw e;
+        }
     }
 
     /**
@@ -252,22 +235,27 @@
     private void linkFiles(IncrementalStorage sourceStorage, File sourceAbsolutePath,
             String sourceRelativePath, IncrementalStorage targetStorage,
             String targetRelativePath) throws IOException {
-        targetStorage.makeDirectory(targetRelativePath);
-        final File[] entryList = sourceAbsolutePath.listFiles();
-        for (int i = 0; i < entryList.length; i++) {
-            final File entry = entryList[i];
-            final String entryName = entryList[i].getName();
-            final String sourceEntryRelativePath =
-                    sourceRelativePath.isEmpty() ? entryName : sourceRelativePath + "/" + entryName;
-            final String targetEntryRelativePath = targetRelativePath + "/" + entryName;
-            if (entry.isFile()) {
-                sourceStorage.makeLink(
-                        sourceEntryRelativePath, targetStorage, targetEntryRelativePath);
-            } else if (entry.isDirectory()) {
-                linkFiles(sourceStorage, entry, sourceEntryRelativePath, targetStorage,
-                        targetEntryRelativePath);
+        final Path sourceBase = sourceAbsolutePath.toPath().resolve(sourceRelativePath);
+        final Path targetRelative = Paths.get(targetRelativePath);
+        Files.walkFileTree(sourceAbsolutePath.toPath(), new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+                    throws IOException {
+                final Path relativeDir = sourceBase.relativize(dir);
+                targetStorage.makeDirectory(targetRelative.resolve(relativeDir).toString());
+                return FileVisitResult.CONTINUE;
             }
-        }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                    throws IOException {
+                final Path relativeFile = sourceBase.relativize(file);
+                sourceStorage.makeLink(
+                        file.toAbsolutePath().toString(), targetStorage,
+                        targetRelative.resolve(relativeFile).toString());
+                return FileVisitResult.CONTINUE;
+            }
+        });
     }
 
     /**