1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski// Copyright 2006 The Android Open Source Project
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski// Package assets into Zip files.
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski//
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "Main.h"
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "AaptAssets.h"
8fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski#include "OutputSet.h"
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "ResourceTable.h"
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include "ResourceFilter.h"
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <androidfw/misc.h>
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <utils/Log.h>
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <utils/threads.h>
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <utils/List.h>
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <utils/Errors.h>
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <utils/misc.h>
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <sys/types.h>
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <dirent.h>
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <ctype.h>
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski#include <errno.h>
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiusing namespace android;
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic const char* kExcludeExtension = ".EXCLUDE";
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/* these formats are already compressed, or don't compress well */
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskistatic const char* kNoCompressExt[] = {
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ".jpg", ".jpeg", ".png", ".gif",
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ".wav", ".mp2", ".mp3", ".ogg", ".aac",
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ".amr", ".awb", ".wma", ".wmv"
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski};
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/* fwd decls, so I can write this downward */
40fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskissize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet);
41fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskibool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file);
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskibool okayToCompress(Bundle* bundle, const String8& pathName);
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskissize_t processJarFiles(Bundle* bundle, ZipFile* zip);
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * The directory hierarchy looks like this:
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * "outputDir" and "assetRoot" are existing directories.
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * On success, "bundle->numPackages" will be the number of Zip packages
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * we created.
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
52fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskistatus_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet)
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    #if BENCHMARK
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    long startAPKTime = clock();
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    #endif /* BENCHMARK */
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t result = NO_ERROR;
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ZipFile* zip = NULL;
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int count;
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //bundle->setPackageCount(0);
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /*
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Prep the Zip archive.
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * If the file already exists, fail unless "update" or "force" is set.
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * If "update" is set, update the contents of the existing archive.
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Else, if "force" is set, remove the existing archive.
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    FileType fileType = getFileType(outputFile.string());
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fileType == kFileTypeNonexistent) {
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // okay, create it below
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (fileType == kFileTypeRegular) {
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (bundle->getUpdate()) {
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // okay, open it below
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if (bundle->getForce()) {
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (unlink(outputFile.string()) != 0) {
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                fprintf(stderr, "ERROR: unable to remove '%s': %s\n", outputFile.string(),
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        strerror(errno));
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                goto bail;
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            fprintf(stderr, "ERROR: '%s' exists (use '-f' to force overwrite)\n",
86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    outputFile.string());
87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            goto bail;
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "ERROR: '%s' exists and is not a regular file\n", outputFile.string());
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("%s '%s'\n", (fileType == kFileTypeNonexistent) ? "Creating" : "Opening",
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outputFile.string());
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t status;
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    zip = new ZipFile;
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status = zip->open(outputFile.string(), ZipFile::kOpenReadWrite | ZipFile::kOpenCreate);
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (status != NO_ERROR) {
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "ERROR: unable to open '%s' as Zip file for writing\n",
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outputFile.string());
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Writing all files...\n");
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
112fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski    count = processAssets(bundle, zip, outputSet);
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (count < 0) {
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outputFile.string());
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        result = count;
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose()) {
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Generated %d file%s\n", count, (count==1) ? "" : "s");
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    count = processJarFiles(bundle, zip);
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (count < 0) {
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "ERROR: unable to process jar files while packaging '%s'\n",
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                outputFile.string());
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        result = count;
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getVerbose())
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Included %d file%s from jar/zip files.\n", count, (count==1) ? "" : "s");
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    result = NO_ERROR;
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /*
138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Check for cruft.  We set the "marked" flag on all entries we created
139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * or decided not to update.  If the entry isn't already slated for
140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * deletion, remove it now.
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    {
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (bundle->getVerbose())
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("Checking for deleted files\n");
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int i, removed = 0;
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (i = 0; i < zip->getNumEntries(); i++) {
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            ZipEntry* entry = zip->getEntryByIndex(i);
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (!entry->getMarked() && entry->getDeleted()) {
150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (bundle->getVerbose()) {
151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    printf("      (removing crufty '%s')\n",
152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        entry->getFileName());
153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                zip->remove(entry);
155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                removed++;
156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (bundle->getVerbose() && removed > 0)
159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("Removed %d file%s\n", removed, (removed==1) ? "" : "s");
160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /* tell Zip lib to process deletions and other pending changes */
163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    result = zip->flush();
164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (result != NO_ERROR) {
165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "ERROR: Zip flush failed, archive may be hosed\n");
166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        goto bail;
167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /* anything here? */
170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (zip->getNumEntries() == 0) {
171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (bundle->getVerbose()) {
172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("Archive is empty -- removing %s\n", outputFile.getPathLeaf().string());
173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        delete zip;        // close the file so we can remove it in Win32
175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        zip = NULL;
176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (unlink(outputFile.string()) != 0) {
177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.string());
178282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // If we've been asked to generate a dependency file for the .ap_ package,
182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // do so here
183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getGenDependencies()) {
184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // The dependency file gets output to the same directory
185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // as the specified output file with an additional .d extension.
186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // e.g. bin/resources.ap_.d
187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String8 dependencyFile = outputFile;
188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        dependencyFile.append(".d");
189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        FILE* fp = fopen(dependencyFile.string(), "a");
191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Add this file to the dependency file
192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(fp, "%s \\\n", outputFile.string());
193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fclose(fp);
194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
195282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
196282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    assert(result == NO_ERROR);
197282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
198282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskibail:
199282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    delete zip;        // must close before remove in Win32
200282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (result != NO_ERROR) {
201282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (bundle->getVerbose()) {
202282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("Removing %s due to earlier failures\n", outputFile.string());
203282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
204282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (unlink(outputFile.string()) != 0) {
205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.string());
206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
208282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (result == NO_ERROR && bundle->getVerbose())
210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        printf("Done!\n");
211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    #if BENCHMARK
213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    fprintf(stdout, "BENCHMARK: End APK Bundling. Time Elapsed: %f ms \n",(clock() - startAPKTime)/1000.0);
214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    #endif /* BENCHMARK */
215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return result;
216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
218fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskissize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet)
219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ssize_t count = 0;
221fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski    const std::set<OutputEntry>& entries = outputSet->getEntries();
222fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski    std::set<OutputEntry>::const_iterator iter = entries.begin();
223fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski    for (; iter != entries.end(); iter++) {
224fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski        const OutputEntry& entry = *iter;
225fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski        if (entry.getFile() == NULL) {
226fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski            fprintf(stderr, "warning: null file being processed.\n");
227fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski        } else {
228fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski            String8 storagePath(entry.getPath());
229fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski            storagePath.convertToResPath();
230fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski            if (!processFile(bundle, zip, storagePath, entry.getFile())) {
231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return UNKNOWN_ERROR;
232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            count++;
234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return count;
237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Process a regular file, adding it to the archive if appropriate.
241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * If we're in "update" mode, and the file already exists in the archive,
243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * delete the existing entry before adding the new one.
244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskibool processFile(Bundle* bundle, ZipFile* zip,
246fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski                 String8 storageName, const sp<const AaptFile>& file)
247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const bool hasData = file->hasData();
249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ZipEntry* entry;
251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    bool fromGzip = false;
252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t result;
253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /*
255282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * See if the filename ends in ".EXCLUDE".  We can't use
256282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * String8::getPathExtension() because the length of what it considers
257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * to be an extension is capped.
258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * The Asset Manager doesn't check for ".EXCLUDE" in Zip archives,
260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * so there's no value in adding them (and it makes life easier on
261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * the AssetManager lib if we don't).
262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * NOTE: this restriction has been removed.  If you're in this code, you
264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * should clean this up, but I'm in here getting rid of Path Name, and I
265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * don't want to make other potentially breaking changes --joeo
266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int fileNameLen = storageName.length();
268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int excludeExtensionLen = strlen(kExcludeExtension);
269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fileNameLen > excludeExtensionLen
270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            && (0 == strcmp(storageName.string() + (fileNameLen - excludeExtensionLen),
271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            kExcludeExtension))) {
272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fprintf(stderr, "warning: '%s' not added to Zip\n", storageName.string());
273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return true;
274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
275282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (strcasecmp(storageName.getPathExtension().string(), ".gz") == 0) {
277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        fromGzip = true;
278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        storageName = storageName.getBasePath();
279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (bundle->getUpdate()) {
282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        entry = zip->getEntryByName(storageName.string());
283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (entry != NULL) {
284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            /* file already exists in archive; there can be only one */
285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (entry->getMarked()) {
286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                fprintf(stderr,
287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        "ERROR: '%s' exists twice (check for with & w/o '.gz'?)\n",
288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        file->getPrintableSource().string());
289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return false;
290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (!hasData) {
292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                const String8& srcName = file->getSourceFile();
293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                time_t fileModWhen;
294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                fileModWhen = getFileModDate(srcName.string());
295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (fileModWhen == (time_t) -1) { // file existence tested earlier,
296282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return false;                 //  not expecting an error here
297282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
298282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
299282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (fileModWhen > entry->getModWhen()) {
300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // mark as deleted so add() will succeed
301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (bundle->getVerbose()) {
302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        printf("      (removing old '%s')\n", storageName.string());
303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    zip->remove(entry);
306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } else {
307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // version in archive is newer
308282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (bundle->getVerbose()) {
309282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        printf("      (not updating '%s')\n", storageName.string());
310282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
311282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    entry->setMarked(true);
312282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return true;
313282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
314282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
315282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // Generated files are always replaced.
316282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                zip->remove(entry);
317282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
318282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
319282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
320282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
321282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    //android_setMinPriority(NULL, ANDROID_LOG_VERBOSE);
322282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
323282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (fromGzip) {
324282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        result = zip->addGzip(file->getSourceFile().string(), storageName.string(), &entry);
325282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else if (!hasData) {
326282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /* don't compress certain files, e.g. PNGs */
327282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int compressionMethod = bundle->getCompressionMethod();
328282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (!okayToCompress(bundle, storageName)) {
329282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            compressionMethod = ZipEntry::kCompressStored;
330282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
331282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        result = zip->add(file->getSourceFile().string(), storageName.string(), compressionMethod,
332282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            &entry);
333282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
334282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        result = zip->add(file->getData(), file->getSize(), storageName.string(),
335282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                           file->getCompressionMethod(), &entry);
336282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
337282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (result == NO_ERROR) {
338282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (bundle->getVerbose()) {
339282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            printf("      '%s'%s", storageName.string(), fromGzip ? " (from .gz)" : "");
340282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (entry->getCompressionMethod() == ZipEntry::kCompressStored) {
341282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (not compressed)\n");
342282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
343282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                printf(" (compressed %d%%)\n", calcPercent(entry->getUncompressedLen(),
344282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            entry->getCompressedLen()));
345282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
346282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
347282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        entry->setMarked(true);
348282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    } else {
349282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (result == ALREADY_EXISTS) {
350282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            fprintf(stderr, "      Unable to add '%s': file already in archive (try '-u'?)\n",
351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    file->getPrintableSource().string());
352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
353fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski            fprintf(stderr, "      Unable to add '%s': Zip add failed (%d)\n",
354fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski                    file->getPrintableSource().string(), result);
355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return false;
357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return true;
360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Determine whether or not we want to try to compress this file based
364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * on the file extension.
365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskibool okayToCompress(Bundle* bundle, const String8& pathName)
367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    String8 ext = pathName.getPathExtension();
369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    int i;
370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (ext.length() == 0)
372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return true;
373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
374282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i = 0; i < NELEM(kNoCompressExt); i++) {
375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (strcasecmp(ext.string(), kNoCompressExt[i]) == 0)
376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return false;
377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const android::Vector<const char*>& others(bundle->getNoCompressExtensions());
380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (i = 0; i < (int)others.size(); i++) {
381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        const char* str = others[i];
382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int pos = pathName.length() - strlen(str);
383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (pos < 0) {
384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            continue;
385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        const char* path = pathName.string();
387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (strcasecmp(path + pos, str) == 0) {
388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return false;
389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return true;
393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskibool endsWith(const char* haystack, const char* needle)
396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t a = strlen(haystack);
398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t b = strlen(needle);
399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    if (a < b) return false;
400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return strcasecmp(haystack+(a-b), needle) == 0;
401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskissize_t processJarFile(ZipFile* jar, ZipFile* out)
404282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t err;
406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t N = jar->getNumEntries();
407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t count = 0;
408282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (size_t i=0; i<N; i++) {
409282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        ZipEntry* entry = jar->getEntryByIndex(i);
410282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        const char* storageName = entry->getFileName();
411282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (endsWith(storageName, ".class")) {
412282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int compressionMethod = entry->getCompressionMethod();
413282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            size_t size = entry->getUncompressedLen();
414282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            const void* data = jar->uncompress(entry);
415282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (data == NULL) {
416282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                fprintf(stderr, "ERROR: unable to uncompress entry '%s'\n",
417282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    storageName);
418282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return -1;
419282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
420282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            out->add(data, size, storageName, compressionMethod, NULL);
421282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            free((void*)data);
422282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
423282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        count++;
424282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
425282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return count;
426282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
427282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
428282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskissize_t processJarFiles(Bundle* bundle, ZipFile* zip)
429282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski{
430282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    status_t err;
431282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    ssize_t count = 0;
432282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    const android::Vector<const char*>& jars = bundle->getJarFiles();
433282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
434282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    size_t N = jars.size();
435282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    for (size_t i=0; i<N; i++) {
436282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        ZipFile jar;
437282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        err = jar.open(jars[i], ZipFile::kOpenReadOnly);
438282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (err != 0) {
439282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            fprintf(stderr, "ERROR: unable to open '%s' as a zip file: %d\n",
440282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                jars[i], err);
441282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return err;
442282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
443282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        err += processJarFile(&jar, zip);
444282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (err < 0) {
445282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            fprintf(stderr, "ERROR: unable to process '%s'\n", jars[i]);
446282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return err;
447282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
448282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        count += err;
449282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
450282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
451282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    return count;
452282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
453