Fully implement "install" and "install-write" in PackageManagerShellCommand.

We can use the new mechanism to ask the calling shell to open
a file in order to implement the rest of these commands, allowing
you to give the path to an apk to install.  That API is thus
extended to allow you to open readable files, not just opening
file for writing.

Doing this however means we no longer can pass a file path to
AssetManager for the apk to parse, we only have an already open
fd for that.  Extending AssetManager to allow adding apks from
fds is not that hard, however, since the underlying zip library
already supports this.

This main thing this changes is in AssetManager.cpp where we
retrieve the open zip file for a particular apk that has been
added.  This used to look up the zip file by path every time
it was needed, but that won't work anymore now that we can have
things added by fd.  Instead, we keep track of each opened zip
in the AssetManager, so we can just directly retrieve it from
the asset_path representing the item that was added.  As a
side-effect, this means for normal paths we no longer need to
look up by name, but just have the opened zip file directly
accessible.  (This is probably good, but it does mean that we
no longer run the logic of seeing if the zip file's timestamp
has changed and re-opening it if it has.  We probably shouldn't
be relying on that for an active AssetManager anyway, and maybe
it is even good that we don't allow the zip file to change
under it?)

A follow-up change will finally remove the Pm.java implementation
and turn the pm "command" into a simple shell script that runs
cmd package.

Test: manual

Change-Id: Ie103e3bdaa5b706796cc329254f2638151a3924f
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 5603508..3c8736e 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -150,6 +150,14 @@
         ALOGI("Destroying AssetManager in %p #%d\n", this, count);
     }
 
+    // Manually close any fd paths for which we have not yet opened their zip (which
+    // will take ownership of the fd and close it when done).
+    for (size_t i=0; i<mAssetPaths.size(); i++) {
+        if (mAssetPaths[i].rawFd >= 0 && mAssetPaths[i].zip == NULL) {
+            close(mAssetPaths[i].rawFd);
+        }
+    }
+
     delete mConfig;
     delete mResources;
 
@@ -280,7 +288,35 @@
     }
 
     return true;
- }
+}
+
+bool AssetManager::addAssetFd(
+        int fd, const String8& debugPathName, int32_t* cookie, bool appAsLib,
+        bool assume_ownership) {
+    AutoMutex _l(mLock);
+
+    asset_path ap;
+
+    ap.path = debugPathName;
+    ap.rawFd = fd;
+    ap.type = kFileTypeRegular;
+    ap.assumeOwnership = assume_ownership;
+
+    ALOGV("In %p Asset fd %d name: %s", this, fd, ap.path.string());
+
+    mAssetPaths.add(ap);
+
+    // new paths are always added at the end
+    if (cookie) {
+        *cookie = static_cast<int32_t>(mAssetPaths.size());
+    }
+
+    if (mResources != NULL) {
+        appendPathToResTable(ap, appAsLib);
+    }
+
+    return true;
+}
 
 bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath,
         uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize)
@@ -505,7 +541,7 @@
     Asset* idmap = openIdmapLocked(ap);
     size_t nextEntryIdx = mResources->getTableCount();
     ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
-    if (ap.type != kFileTypeDirectory) {
+    if (ap.type != kFileTypeDirectory && ap.rawFd < 0) {
         if (nextEntryIdx == 0) {
             // The first item is typically the framework resources,
             // which we want to avoid parsing every time.
@@ -738,6 +774,8 @@
 {
     Asset* pAsset = NULL;
 
+    ALOGV("openNonAssetInPath: name=%s type=%d fd=%d", fileName, ap.type, ap.rawFd);
+
     /* look at the filesystem on disk */
     if (ap.type == kFileTypeDirectory) {
         String8 path(ap.path);
@@ -752,7 +790,7 @@
         }
 
         if (pAsset != NULL) {
-            //printf("FOUND NA '%s' on disk\n", fileName);
+            ALOGV("FOUND NA '%s' on disk", fileName);
             pAsset->setAssetSource(path);
         }
 
@@ -763,10 +801,10 @@
         /* check the appropriate Zip file */
         ZipFileRO* pZip = getZipFileLocked(ap);
         if (pZip != NULL) {
-            //printf("GOT zip, checking NA '%s'\n", (const char*) path);
+            ALOGV("GOT zip, checking NA '%s'", (const char*) path);
             ZipEntryRO entry = pZip->findEntryByName(path.string());
             if (entry != NULL) {
-                //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon);
+                ALOGV("FOUND NA in Zip file for %s", (const char*) path);
                 pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
                 pZip->releaseEntry(entry);
             }
@@ -817,7 +855,17 @@
 {
     ALOGV("getZipFileLocked() in %p\n", this);
 
-    return mZipSet.getZip(ap.path);
+    if (ap.zip != NULL) {
+        return ap.zip->getZip();
+    }
+
+    if (ap.rawFd < 0) {
+        ap.zip = mZipSet.getSharedZip(ap.path);
+    } else {
+        ap.zip = SharedZip::create(ap.rawFd, ap.path);
+
+    }
+    return ap.zip != NULL ? ap.zip->getZip() : NULL;
 }
 
 /*
@@ -1374,6 +1422,21 @@
     }
 }
 
+AssetManager::SharedZip::SharedZip(int fd, const String8& path)
+    : mPath(path), mZipFile(NULL), mModWhen(0),
+      mResourceTableAsset(NULL), mResourceTable(NULL)
+{
+    if (kIsDebug) {
+        ALOGI("Creating SharedZip %p fd=%d %s\n", this, fd, (const char*)mPath);
+    }
+    ALOGV("+++ opening zip fd=%d '%s'\n", fd, mPath.string());
+    mZipFile = ZipFileRO::openFd(fd, mPath.string());
+    if (mZipFile == NULL) {
+        ::close(fd);
+        ALOGD("failed to open Zip archive fd=%d '%s'\n", fd, mPath.string());
+    }
+}
+
 sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path,
         bool createIfNotPresent)
 {
@@ -1389,7 +1452,11 @@
     zip = new SharedZip(path, modWhen);
     gOpen.add(path, zip);
     return zip;
+}
 
+sp<AssetManager::SharedZip> AssetManager::SharedZip::create(int fd, const String8& path)
+{
+    return new SharedZip(fd, path);
 }
 
 ZipFileRO* AssetManager::SharedZip::getZip()
@@ -1500,19 +1567,23 @@
     mZipFile.editItemAt(idx) = NULL;
 }
 
-
 /*
  * Retrieve the appropriate Zip file from the set.
  */
 ZipFileRO* AssetManager::ZipSet::getZip(const String8& path)
 {
+    return getSharedZip(path)->getZip();
+}
+
+const sp<AssetManager::SharedZip> AssetManager::ZipSet::getSharedZip(const String8& path)
+{
     int idx = getIndex(path);
     sp<SharedZip> zip = mZipFile[idx];
     if (zip == NULL) {
         zip = SharedZip::get(path);
         mZipFile.editItemAt(idx) = zip;
     }
-    return zip->getZip();
+    return zip;
 }
 
 Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path)
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 49fe8a2..6e2ca60c 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -55,7 +55,9 @@
 
 ZipFileRO::~ZipFileRO() {
     CloseArchive(mHandle);
-    free(mFileName);
+    if (mFileName != NULL) {
+        free(mFileName);
+    }
 }
 
 /*
@@ -76,6 +78,20 @@
 }
 
 
+/* static */ ZipFileRO* ZipFileRO::openFd(int fd, const char* debugFileName,
+        bool assume_ownership)
+{
+    ZipArchiveHandle handle;
+    const int32_t error = OpenArchiveFd(fd, debugFileName, &handle, assume_ownership);
+    if (error) {
+        ALOGW("Error opening archive fd %d %s: %s", fd, debugFileName, ErrorCodeString(error));
+        CloseArchive(handle);
+        return NULL;
+    }
+
+    return new ZipFileRO(handle, strdup(debugFileName));
+}
+
 ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const
 {
     _ZipEntryRO* data = new _ZipEntryRO;
@@ -139,7 +155,8 @@
                                    prefix ? &pe : NULL,
                                    suffix ? &se : NULL);
     if (error) {
-        ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error));
+        ALOGW("Could not start iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
+                ErrorCodeString(error));
         delete ze;
         return false;
     }
@@ -154,7 +171,8 @@
     int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name));
     if (error) {
         if (error != -1) {
-            ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error));
+            ALOGW("Error iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
+                    ErrorCodeString(error));
         }
         return NULL;
     }
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index 0441b9d..4254614 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -92,6 +92,20 @@
     bool addOverlayPath(const String8& path, int32_t* cookie);
 
     /*
+     * Add a new source for assets from an already open file descriptor.
+     * This does not give full AssetManager functionality for these assets,
+     * since the origin of the file is not known for purposes of sharing,
+     * overlay resolution, and other features.  However it does allow you
+     * to do simple access to the contents of the given fd as an apk file.
+     *
+     * Returns "true" on success, "false" on failure.  If 'cookie' is non-NULL,
+     * then on success, *cookie is set to the value corresponding to the
+     * newly-added asset source.
+     */
+    bool addAssetFd(int fd, const String8& debugPathName, int32_t* cookie,
+        bool appAsLib=false, bool assume_ownership=true);
+
+    /*
      * Convenience for adding the standard system assets.  Uses the
      * ANDROID_ROOT environment variable to find them.
      */
@@ -195,15 +209,20 @@
         uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize);
 
 private:
+    class SharedZip;
+
     struct asset_path
     {
-        asset_path() : path(""), type(kFileTypeRegular), idmap(""),
-                       isSystemOverlay(false), isSystemAsset(false) {}
+        asset_path() : path(""), rawFd(-1), type(kFileTypeRegular), idmap(""),
+                       isSystemOverlay(false), isSystemAsset(false), assumeOwnership(false) {}
         String8 path;
+        int rawFd;
         FileType type;
         String8 idmap;
         bool isSystemOverlay;
         bool isSystemAsset;
+        bool assumeOwnership;
+        mutable sp<SharedZip> zip;
     };
 
     Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode,
@@ -238,6 +257,7 @@
     class SharedZip : public RefBase {
     public:
         static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true);
+        static sp<SharedZip> create(int fd, const String8& path);
 
         ZipFileRO* getZip();
 
@@ -257,6 +277,7 @@
 
     private:
         SharedZip(const String8& path, time_t modWhen);
+        SharedZip(int fd, const String8& path);
         SharedZip(); // <-- not implemented
 
         String8 mPath;
@@ -290,6 +311,8 @@
          */
         ZipFileRO* getZip(const String8& path);
 
+        const sp<SharedZip> getSharedZip(const String8& path);
+
         Asset* getZipResourceTableAsset(const String8& path);
         Asset* setZipResourceTableAsset(const String8& path, Asset* asset);
 
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index 7680342..03154d0 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -80,6 +80,12 @@
     static ZipFileRO* open(const char* zipFileName);
 
     /*
+     * Open an archive from an already open file descriptor.
+     */
+    static ZipFileRO* openFd(int fd, const char* debugFileName,
+        bool assume_ownership = true);
+
+    /*
      * Find an entry, by name.  Returns the entry identifier, or NULL if
      * not found.
      */