aapt: Preprocess images in parallel.

Currently hardcoded to use up to 4 threads.

This change substantially reduces the amount of time spent
preprocessing framework resources to just a few seconds.

Change-Id: I02fdd283fb529a152aeb22ac87f278779fd77983
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 8e3a1c9..daeadc0 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -83,7 +83,7 @@
     bool getForce(void) const { return mForce; }
     void setForce(bool val) { mForce = val; }
     void setGrayscaleTolerance(int val) { mGrayscaleTolerance = val; }
-    int  getGrayscaleTolerance() { return mGrayscaleTolerance; }
+    int  getGrayscaleTolerance() const { return mGrayscaleTolerance; }
     bool getMakePackageDirs(void) const { return mMakePackageDirs; }
     void setMakePackageDirs(bool val) { mMakePackageDirs = val; }
     bool getUpdate(void) const { return mUpdate; }
@@ -166,14 +166,14 @@
     void setExtraPackages(const char* val) { mExtraPackages = val; }
     const char* getMaxResVersion() const { return mMaxResVersion; }
     void setMaxResVersion(const char * val) { mMaxResVersion = val; }
-    bool getDebugMode() { return mDebugMode; }
+    bool getDebugMode() const { return mDebugMode; }
     void setDebugMode(bool val) { mDebugMode = val; }
-    bool getNonConstantId() { return mNonConstantId; }
+    bool getNonConstantId() const { return mNonConstantId; }
     void setNonConstantId(bool val) { mNonConstantId = val; }
     const char* getProduct() const { return mProduct; }
     void setProduct(const char * val) { mProduct = val; }
     void setUseCrunchCache(bool val) { mUseCrunchCache = val; }
-    bool getUseCrunchCache() { return mUseCrunchCache; }
+    bool getUseCrunchCache() const { return mUseCrunchCache; }
 
     /*
      * Set and get the file specification.
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 6402e3c..2b9b056 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -964,7 +964,7 @@
                  compression_type));
 }
 
-status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
+status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
                          const sp<AaptFile>& file, String8* outNewLeafName)
 {
     String8 ext(file->getPath().getPathExtension());
@@ -1084,7 +1084,7 @@
     return error;
 }
 
-status_t preProcessImageToCache(Bundle* bundle, String8 source, String8 dest)
+status_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest)
 {
     png_structp read_ptr = NULL;
     png_infop read_info = NULL;
diff --git a/tools/aapt/Images.h b/tools/aapt/Images.h
index 4816905..91b6554 100644
--- a/tools/aapt/Images.h
+++ b/tools/aapt/Images.h
@@ -15,10 +15,10 @@
 
 using android::String8;
 
-status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
+status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
                          const sp<AaptFile>& file, String8* outNewLeafName);
 
-status_t preProcessImageToCache(Bundle* bundle, String8 source, String8 dest);
+status_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest);
 
 status_t postProcessImage(const sp<AaptAssets>& assets,
                           ResourceTable* table, const sp<AaptFile>& file);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 7eaf528..b9ec30c 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -14,6 +14,8 @@
 #include "FileFinder.h"
 #include "CacheUpdater.h"
 
+#include <utils/WorkQueue.h>
+
 #if HAVE_PRINTF_ZD
 #  define ZD "%zd"
 #  define ZD_TYPE ssize_t
@@ -24,6 +26,9 @@
 
 #define NOISY(x) // x
 
+// Number of threads to use for preprocessing images.
+static const size_t MAX_THREADS = 4;
+
 // ==========================================================================
 // ==========================================================================
 // ==========================================================================
@@ -302,21 +307,52 @@
     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
 }
 
-static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
+class PreProcessImageWorkUnit : public WorkQueue::WorkUnit {
+public:
+    PreProcessImageWorkUnit(const Bundle* bundle, const sp<AaptAssets>& assets,
+            const sp<AaptFile>& file, volatile bool* hasErrors) :
+            mBundle(bundle), mAssets(assets), mFile(file), mHasErrors(hasErrors) {
+    }
+
+    virtual bool run() {
+        status_t status = preProcessImage(mBundle, mAssets, mFile, NULL);
+        if (status) {
+            *mHasErrors = true;
+        }
+        return true; // continue even if there are errors
+    }
+
+private:
+    const Bundle* mBundle;
+    sp<AaptAssets> mAssets;
+    sp<AaptFile> mFile;
+    volatile bool* mHasErrors;
+};
+
+static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& assets,
                           const sp<ResourceTypeSet>& set, const char* type)
 {
-    bool hasErrors = false;
+    volatile bool hasErrors = false;
     ssize_t res = NO_ERROR;
     if (bundle->getUseCrunchCache() == false) {
+        WorkQueue wq(MAX_THREADS, false);
         ResourceDirIterator it(set, String8(type));
-        Vector<sp<AaptFile> > newNameFiles;
-        Vector<String8> newNamePaths;
         while ((res=it.next()) == NO_ERROR) {
-            res = preProcessImage(bundle, assets, it.getFile(), NULL);
-            if (res < NO_ERROR) {
+            PreProcessImageWorkUnit* w = new PreProcessImageWorkUnit(
+                    bundle, assets, it.getFile(), &hasErrors);
+            status_t status = wq.schedule(w);
+            if (status) {
+                fprintf(stderr, "preProcessImages failed: schedule() returned %d\n", status);
                 hasErrors = true;
+                delete w;
+                break;
             }
         }
+        status_t status = wq.finish();
+        if (status) {
+            fprintf(stderr, "preProcessImages failed: finish() returned %d\n", status);
+            hasErrors = true;
+        }
     }
     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
 }