Merge "DSU service: Pipeline the installation task to improve performance" am: 309fad06f6 am: 1991fbc73b am: 31de7473e8

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2045078

Change-Id: If1a1cc1ddcccc2fda857204b6e59a888a7b2ce62
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 2af7e00..38d851e 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -41,6 +41,10 @@
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -51,8 +55,8 @@
     private static final String TAG = "InstallationAsyncTask";
 
     private static final int MIN_SHARED_MEMORY_SIZE = 8 << 10; // 8KiB
-    private static final int MAX_SHARED_MEMORY_SIZE = 1024 << 10; // 1MiB
-    private static final int DEFAULT_SHARED_MEMORY_SIZE = 64 << 10; // 64KiB
+    private static final int MAX_SHARED_MEMORY_SIZE = 8 << 20; // 8MiB
+    private static final int DEFAULT_SHARED_MEMORY_SIZE = 512 << 10; // 512KiB
     private static final String SHARED_MEMORY_SIZE_PROP =
             "dynamic_system.data_transfer.shared_memory.size";
 
@@ -488,7 +492,7 @@
         installWritablePartition("userdata", mUserdataSize);
     }
 
-    private void installImages() throws IOException, ImageValidationException {
+    private void installImages() throws ExecutionException, IOException, ImageValidationException {
         if (mStream != null) {
             if (mIsZip) {
                 installStreamingZipUpdate();
@@ -500,7 +504,8 @@
         }
     }
 
-    private void installStreamingGzUpdate() throws IOException, ImageValidationException {
+    private void installStreamingGzUpdate()
+            throws ExecutionException, IOException, ImageValidationException {
         Log.d(TAG, "To install a streaming GZ update");
         installImage("system", mSystemSize, new GZIPInputStream(mStream));
     }
@@ -528,7 +533,8 @@
         return total;
     }
 
-    private void installStreamingZipUpdate() throws IOException, ImageValidationException {
+    private void installStreamingZipUpdate()
+            throws ExecutionException, IOException, ImageValidationException {
         Log.d(TAG, "To install a streaming ZIP update");
 
         ZipInputStream zis = new ZipInputStream(mStream);
@@ -548,7 +554,8 @@
         }
     }
 
-    private void installLocalZipUpdate() throws IOException, ImageValidationException {
+    private void installLocalZipUpdate()
+            throws ExecutionException, IOException, ImageValidationException {
         Log.d(TAG, "To install a local ZIP update");
 
         Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -569,7 +576,7 @@
     }
 
     private void installImageFromAnEntry(ZipEntry entry, InputStream is)
-            throws IOException, ImageValidationException {
+            throws ExecutionException, IOException, ImageValidationException {
         String name = entry.getName();
 
         Log.d(TAG, "ZipEntry: " + name);
@@ -581,7 +588,7 @@
     }
 
     private void installImage(String partitionName, long uncompressedSize, InputStream is)
-            throws IOException, ImageValidationException {
+            throws ExecutionException, IOException, ImageValidationException {
 
         SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
 
@@ -637,27 +644,51 @@
         long prevInstalledSize = 0;
         long installedSize = 0;
         byte[] bytes = new byte[memoryFile.length()];
-        int numBytesRead;
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<Boolean> submitPromise = null;
 
-        while ((numBytesRead = sis.read(bytes, 0, bytes.length)) != -1) {
+        while (true) {
+            final int numBytesRead = sis.read(bytes, 0, bytes.length);
+
+            if (submitPromise != null) {
+                // Wait until the previous submit task is complete.
+                while (true) {
+                    try {
+                        if (!submitPromise.get()) {
+                            throw new IOException("Failed submitFromAshmem() to DynamicSystem");
+                        }
+                        break;
+                    } catch (InterruptedException e) {
+                        // Ignore.
+                    }
+                }
+
+                // Publish the progress of the previous submit task.
+                if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
+                    publishProgress(installedSize);
+                    prevInstalledSize = installedSize;
+                }
+            }
+
+            // Ensure the previous submit task (submitPromise) is complete before exiting the loop.
+            if (numBytesRead < 0) {
+                break;
+            }
+
             if (isCancelled()) {
                 return;
             }
 
             memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
+            submitPromise =
+                    executor.submit(() -> mInstallationSession.submitFromAshmem(numBytesRead));
 
-            if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
-                throw new IOException("Failed write() to DynamicSystem");
-            }
-
+            // Even though we update the bytes counter here, the actual progress is updated only
+            // after the submit task (submitPromise) is complete.
             installedSize += numBytesRead;
-
-            if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
-                publishProgress(installedSize);
-                prevInstalledSize = installedSize;
-            }
         }
 
+        // Ensure a 100% mark is published.
         if (prevInstalledSize != partitionSize) {
             publishProgress(partitionSize);
         }