Shared library resource support

Shared libraries can now export resources for applications
to use.

Exporting resources works the same way the framework exports
resources, by defining the public symbols in res/values/public.xml.

Building a shared library requires aapt to be invoked with the
--shared-lib option. Shared libraries will be assigned a package
ID of 0x00 at build-time. At runtime, all loaded shared libraries
will be assigned a new package ID.

Currently, shared libraries should not import other shared libraries,
as those dependencies will not be loaded at runtime.

At runtime, reflection is used to update the package ID of resource
symbols in the shared library's R class file. The package name of
the R class file is assumed to be the same as the shared library's
package name declared in its manifest. This will be customizable in
a future commit.

See /tests/SharedLibrary/ for examples of a shared library and its
client.

Bug:12724178
Change-Id: I60c0cb8ab87849f8f8a1a13431562fe8603020a7
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index f9a2d19..38bfa00 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1798,6 +1798,11 @@
     return buf;
 }
 
+void* AaptFile::editDataInRange(size_t offset, size_t size)
+{
+    return (void*)(((uint8_t*) editData(offset + size)) + offset);
+}
+
 void* AaptFile::editData(size_t* outSize)
 {
     if (outSize) {
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 336d08b..82dda5f 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -251,6 +251,7 @@
     size_t getSize() const { return mDataSize; }
     void* editData(size_t size);
     void* editData(size_t* outSize = NULL);
+    void* editDataInRange(size_t offset, size_t size);
     void* padData(size_t wordSize);
     status_t writeData(const void* data, size_t size);
     void clearData();
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 49b8b55..d2eccbe 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -64,6 +64,7 @@
           mProduct(NULL), mUseCrunchCache(false), mErrorOnFailedInsert(false),
           mErrorOnMissingConfigEntry(false), mOutputTextSymbols(NULL),
           mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL),
+          mBuildSharedLibrary(false),
           mArgc(0), mArgv(NULL)
         {}
     ~Bundle(void) {}
@@ -188,6 +189,8 @@
     void setSingleCrunchInputFile(const char* val) { mSingleCrunchInputFile = val; }
     const char* getSingleCrunchOutputFile() const { return mSingleCrunchOutputFile; }
     void setSingleCrunchOutputFile(const char* val) { mSingleCrunchOutputFile = val; }
+    bool getBuildSharedLibrary() const { return mBuildSharedLibrary; }
+    void setBuildSharedLibrary(bool val) { mBuildSharedLibrary = val; }
 
     /*
      * Set and get the file specification.
@@ -300,6 +303,7 @@
     const char* mOutputTextSymbols;
     const char* mSingleCrunchInputFile;
     const char* mSingleCrunchOutputFile;
+    bool        mBuildSharedLibrary;
 
     /* file specification */
     int         mArgc;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 33711fa..1cf4783 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -190,6 +190,9 @@
         "       Make the resources ID non constant. This is required to make an R java class\n"
         "       that does not contain the final value but is used to make reusable compiled\n"
         "       libraries that need to access resources.\n"
+        "   --shared-lib\n"
+        "       Make a shared library resource package that can be loaded by an application\n"
+        "       at runtime to access the libraries resources. Implies --non-constant-id.\n"
         "   --error-on-failed-insert\n"
         "       Forces aapt to return an error if it fails to insert values into the manifest\n"
         "       with --debug-mode, --min-sdk-version, --target-sdk-version --version-code\n"
@@ -618,6 +621,9 @@
                     bundle.setProduct(argv[0]);
                 } else if (strcmp(cp, "-non-constant-id") == 0) {
                     bundle.setNonConstantId(true);
+                } else if (strcmp(cp, "-shared-lib") == 0) {
+                    bundle.setNonConstantId(true);
+                    bundle.setBuildSharedLibrary(true);
                 } else if (strcmp(cp, "-no-crunch") == 0) {
                     bundle.setUseCrunchCache(true);
                 } else if (strcmp(cp, "-ignore-assets") == 0) {
diff --git a/tools/aapt/ResourceIdCache.cpp b/tools/aapt/ResourceIdCache.cpp
index e03f4f6..d60a07fc 100644
--- a/tools/aapt/ResourceIdCache.cpp
+++ b/tools/aapt/ResourceIdCache.cpp
@@ -98,10 +98,10 @@
 
 void ResourceIdCache::dump() {
     printf("ResourceIdCache dump:\n");
-    printf("Size: %ld\n", mIdMap.size());
-    printf("Hits:   %ld\n", mHits);
-    printf("Misses: %ld\n", mMisses);
-    printf("(Collisions: %ld)\n", mCollisions);
+    printf("Size: %zd\n", mIdMap.size());
+    printf("Hits:   %zd\n", mHits);
+    printf("Misses: %zd\n", mMisses);
+    printf("(Collisions: %zd)\n", mCollisions);
 }
 
 }
diff --git a/tools/aapt/ResourceIdCache.h b/tools/aapt/ResourceIdCache.h
index e6bcda2..3acdee1 100644
--- a/tools/aapt/ResourceIdCache.h
+++ b/tools/aapt/ResourceIdCache.h
@@ -6,18 +6,20 @@
 #ifndef RESOURCE_ID_CACHE_H
 #define RESOURCE_ID_CACHE_H
 
+#include <utils/String16.h>
+
 namespace android {
 
 class ResourceIdCache {
 public:
-    static uint32_t lookup(const android::String16& package,
-            const android::String16& type,
-            const android::String16& name,
+    static uint32_t lookup(const String16& package,
+            const String16& type,
+            const String16& name,
             bool onlyPublic);
 
-    static uint32_t store(const android::String16& package,
-            const android::String16& type,
-            const android::String16& name,
+    static uint32_t store(const String16& package,
+            const String16& type,
+            const String16& name,
             bool onlyPublic,
             uint32_t resId);
 
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 652998e..0fb2606 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1673,7 +1673,7 @@
 
 ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage)
     : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false),
-      mIsAppPackage(!bundle->getExtending()),
+      mIsAppPackage(!bundle->getExtending()), mIsSharedLibrary(bundle->getBuildSharedLibrary()),
       mNumLocal(0),
       mBundle(bundle)
 {
@@ -1695,8 +1695,9 @@
     const size_t N = incl.getBasePackageCount();
     for (size_t phase=0; phase<2; phase++) {
         for (size_t i=0; i<N; i++) {
-            String16 name(incl.getBasePackageName(i));
+            const String16 name = incl.getBasePackageName(i);
             uint32_t id = incl.getBasePackageId(i);
+
             // First time through: only add base packages (id
             // is not 0); second time through add the other
             // packages.
@@ -1722,7 +1723,7 @@
                 }
             }
             if (id != 0) {
-                NOISY(printf("Including package %s with ID=%d\n",
+                NOISY(fprintf(stderr, "Including package %s with ID=%d\n",
                              String8(name).string(), id));
                 sp<Package> p = new Package(name, id);
                 mPackages.add(name, p);
@@ -2687,6 +2688,9 @@
 
     bool useUTF8 = !bundle->getUTF16StringsOption();
 
+    // The libraries this table references.
+    Vector<sp<Package> > libraryPackages;
+
     // Iterate through all data, collecting all values (strings,
     // references, etc).
     StringPool valueStrings(useUTF8);
@@ -2694,8 +2698,22 @@
     for (pi=0; pi<N; pi++) {
         sp<Package> p = mOrderedPackages.itemAt(pi);
         if (p->getTypes().size() == 0) {
-            // Empty, skip!
+            // Empty, this is an imported package being used as
+            // a shared library. We do not flatten this package.
+            if (p->getAssignedId() != 0x01 && p->getName() != String16("android")) {
+                // This is not the base Android package, and it is a library
+                // so we must add a reference to the library when flattening.
+                libraryPackages.add(p);
+            }
             continue;
+        } else if (p->getAssignedId() == 0x00) {
+            if (!bundle->getBuildSharedLibrary()) {
+                fprintf(stderr, "ERROR: Package %s can not have ID=0x00 unless building a shared library.",
+                        String8(p->getName()).string());
+                return UNKNOWN_ERROR;
+            }
+            // If this is a shared library, we also include ourselves as an entry.
+            libraryPackages.add(p);
         }
 
         StringPool typeStrings(useUTF8);
@@ -2778,7 +2796,7 @@
     }
 
     ssize_t strAmt = 0;
-    
+
     // Now build the array of package chunks.
     Vector<sp<AaptFile> > flatPackages;
     for (pi=0; pi<N; pi++) {
@@ -2827,6 +2845,12 @@
             return amt;
         }
 
+        err = flattenLibraryTable(data, libraryPackages);
+        if (err != NO_ERROR) {
+            fprintf(stderr, "ERROR: failed to write library table\n");
+            return err;
+        }
+
         // Build the type chunks inside of this package.
         for (size_t ti=0; ti<N; ti++) {
             // Retrieve them in the same order as the type string block.
@@ -3053,7 +3077,7 @@
     fprintf(stderr, "**** value strings: %d\n", amt);
     fprintf(stderr, "**** total strings: %d\n", strAmt);
     #endif
-    
+
     for (pi=0; pi<flatPackages.size(); pi++) {
         err = dest->writeData(flatPackages[pi]->getData(),
                               flatPackages[pi]->getSize());
@@ -3078,6 +3102,38 @@
     return NO_ERROR;
 }
 
+status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) {
+    // Write out the library table if necessary
+    if (libs.size() > 0) {
+        NOISY(fprintf(stderr, "Writing library reference table\n"));
+
+        const size_t libStart = dest->getSize();
+        const size_t count = libs.size();
+        ResTable_lib_header* libHeader = (ResTable_lib_header*) dest->editDataInRange(libStart, sizeof(ResTable_lib_header));
+
+        memset(libHeader, 0, sizeof(*libHeader));
+        libHeader->header.type = htods(RES_TABLE_LIBRARY_TYPE);
+        libHeader->header.headerSize = htods(sizeof(*libHeader));
+        libHeader->header.size = htodl(sizeof(*libHeader) + (sizeof(ResTable_lib_entry) * count));
+        libHeader->count = htodl(count);
+
+        // Write the library entries
+        for (size_t i = 0; i < count; i++) {
+            const size_t entryStart = dest->getSize();
+            sp<Package> libPackage = libs[i];
+            NOISY(fprintf(stderr, "  Entry %s -> 0x%02x\n",
+                        String8(libPackage->getName()).string(),
+                        (uint8_t)libPackage->getAssignedId()));
+
+            ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange(entryStart, sizeof(ResTable_lib_entry));
+            memset(entry, 0, sizeof(*entry));
+            entry->packageId = htodl(libPackage->getAssignedId());
+            strcpy16_htod(entry->packageName, libPackage->getName().string());
+        }
+    }
+    return NO_ERROR;
+}
+
 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
 {
     fprintf(fp,
@@ -3847,7 +3903,7 @@
                 return NULL;
             }
             mHaveAppPackage = true;
-            p = new Package(package, 127);
+            p = new Package(package, mIsSharedLibrary ? 0 : 127);
         } else {
             p = new Package(package, mNextPackageId);
         }
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 75005cd..ec8fd175e 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -224,6 +224,7 @@
     status_t validateLocalizations(void);
 
     status_t flatten(Bundle*, const sp<AaptFile>& dest);
+    status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs);
 
     void writePublicDefinitions(const String16& package, FILE* fp);
 
@@ -546,6 +547,7 @@
     uint32_t mNextPackageId;
     bool mHaveAppPackage;
     bool mIsAppPackage;
+    bool mIsSharedLibrary;
     size_t mNumLocal;
     SourcePos mCurrentXmlPos;
     Bundle* mBundle;
diff --git a/tools/aapt/printapk.cpp b/tools/aapt/printapk.cpp
index 4cf73d8..def6e2e 100644
--- a/tools/aapt/printapk.cpp
+++ b/tools/aapt/printapk.cpp
@@ -115,8 +115,8 @@
         size_t basePackageCount = res.getBasePackageCount();
         printf("Base Packages: %d\n", (int)basePackageCount);
         for (size_t bpIndex=0; bpIndex<basePackageCount; bpIndex++) {
-            const char16_t* ch = res.getBasePackageName(bpIndex);
-            String8 s = String8(String16(ch));
+            const String16 ch = res.getBasePackageName(bpIndex);
+            String8 s = String8(ch);
             printf("  [%3d] %s\n", (int)bpIndex, s.string());
         }
 #endif