summaryrefslogtreecommitdiffstats
path: root/tools/aapt
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt')
-rw-r--r--tools/aapt/AaptAssets.cpp151
-rw-r--r--tools/aapt/AaptAssets.h21
-rw-r--r--tools/aapt/AaptConfig.cpp11
-rw-r--r--tools/aapt/Bundle.h17
-rw-r--r--tools/aapt/Command.cpp33
-rw-r--r--tools/aapt/Images.cpp95
-rw-r--r--tools/aapt/Images.h16
-rw-r--r--tools/aapt/Main.cpp47
-rw-r--r--tools/aapt/Main.h9
-rw-r--r--tools/aapt/Package.cpp306
-rw-r--r--tools/aapt/Resource.cpp24
-rw-r--r--tools/aapt/ResourceTable.cpp7
-rw-r--r--tools/aapt/ResourceTable.h3
-rw-r--r--tools/aapt/XMLNode.cpp43
-rw-r--r--tools/aapt/XMLNode.h3
-rw-r--r--tools/aapt/ZipFile.cpp65
-rw-r--r--tools/aapt/ZipFile.h1
-rw-r--r--tools/aapt/tests/ZipReading_test.cpp156
-rw-r--r--tools/aapt/tests/mocks/MockZipEntry.h29
-rw-r--r--tools/aapt/tests/mocks/MockZipFile.h29
20 files changed, 908 insertions, 158 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index d346731..1a5d512 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1048,8 +1048,23 @@ ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
goto bail;
}
totalCount += count;
- }
- else {
+ } else if (type == kFileTypeRegular) {
+ ZipFile* zip = new ZipFile;
+ status_t err = zip->open(String8(res), ZipFile::kOpenReadOnly);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "error opening zip file %s\n", res);
+ delete zip;
+ totalCount = -1;
+ goto bail;
+ }
+
+ count = current->slurpResourceZip(bundle, zip, res);
+ delete zip;
+ if (count < 0) {
+ totalCount = count;
+ goto bail;
+ }
+ } else {
fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
return UNKNOWN_ERROR;
}
@@ -1214,96 +1229,90 @@ bail:
}
ssize_t
-AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename)
+AaptAssets::slurpResourceZip(Bundle* bundle, ZipFile* zip, const char* fullZipPath)
{
+ status_t err = NO_ERROR;
int count = 0;
SortedVector<AaptGroupEntry> entries;
- ZipFile* zip = new ZipFile;
- status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
- if (err != NO_ERROR) {
- fprintf(stderr, "error opening zip file %s\n", filename);
- count = err;
- delete zip;
- return -1;
- }
-
const int N = zip->getNumEntries();
for (int i=0; i<N; i++) {
ZipEntry* entry = zip->getEntryByIndex(i);
- if (entry->getDeleted()) {
+
+ if (!isEntryValid(bundle, entry)) {
continue;
}
- String8 entryName(entry->getFileName());
+ String8 entryName(entry->getFileName()); //ex: /res/drawable/foo.png
+ String8 entryLeaf = entryName.getPathLeaf(); //ex: foo.png
+ String8 entryDirFull = entryName.getPathDir(); //ex: res/drawable
+ String8 entryDir = entryDirFull.getPathLeaf(); //ex: drawable
- String8 dirName = entryName.getPathDir();
- sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
+ err = addEntry(entryName, entryLeaf, entryDirFull, entryDir, String8(fullZipPath), 0);
+ if (err) continue;
- String8 resType;
- AaptGroupEntry kind;
+ count++;
+ }
- String8 remain;
- if (entryName.walkPath(&remain) == kResourceDir) {
- // these are the resources, pull their type out of the directory name
- kind.initFromDirName(remain.walkPath().string(), &resType);
- } else {
- // these are untyped and don't have an AaptGroupEntry
- }
- if (entries.indexOf(kind) < 0) {
- entries.add(kind);
- mGroupEntries.add(kind);
- }
+ return count;
+}
- // use the one from the zip file if they both exist.
- dir->removeFile(entryName.getPathLeaf());
+status_t
+AaptAssets::addEntry(const String8& entryName, const String8& entryLeaf,
+ const String8& /* entryDirFull */, const String8& entryDir,
+ const String8& zipFile, int compressionMethod)
+{
+ AaptGroupEntry group;
+ String8 resType;
+ bool b = group.initFromDirName(entryDir, &resType);
+ if (!b) {
+ fprintf(stderr, "invalid resource directory name: %s\n", entryDir.string());
+ return -1;
+ }
- sp<AaptFile> file = new AaptFile(entryName, kind, resType);
- status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
- if (err != NO_ERROR) {
- fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
- count = err;
- goto bail;
- }
- file->setCompressionMethod(entry->getCompressionMethod());
+ //This will do a cached lookup as well
+ sp<AaptDir> dir = makeDir(resType); //Does lookup as well on mdirs
+ sp<AaptFile> file = new AaptFile(entryName, group, resType, zipFile);
+ file->setCompressionMethod(compressionMethod);
-#if 0
- if (entryName == "AndroidManifest.xml") {
- printf("AndroidManifest.xml\n");
- }
- printf("\n\nfile: %s\n", entryName.string());
-#endif
-
- size_t len = entry->getUncompressedLen();
- void* data = zip->uncompress(entry);
- void* buf = file->editData(len);
- memcpy(buf, data, len);
-
-#if 0
- const int OFF = 0;
- const unsigned char* p = (unsigned char*)data;
- const unsigned char* end = p+len;
- p += OFF;
- for (int i=0; i<32 && p < end; i++) {
- printf("0x%03x ", i*0x10 + OFF);
- for (int j=0; j<0x10 && p < end; j++) {
- printf(" %02x", *p);
- p++;
- }
- printf("\n");
- }
-#endif
+ dir->addLeafFile(entryLeaf, file);
- free(data);
+ sp<AaptDir> rdir = resDir(resType);
+ if (rdir == NULL) {
+ mResDirs.add(dir);
+ }
- count++;
+ return NO_ERROR;
+}
+
+bool AaptAssets::isEntryValid(Bundle* bundle, ZipEntry* entry) {
+ if (entry == NULL) {
+ return false;
}
-bail:
- delete zip;
- return count;
+ if (entry->getDeleted()) {
+ return false;
+ }
+
+ // Entries that are not inside the internal zip path can be ignored
+ if (bundle->getInternalZipPath()) {
+ bool prefixed = (strncmp(entry->getFileName(),
+ bundle->getInternalZipPath(),
+ strlen(bundle->getInternalZipPath())) == 0);
+ if (!prefixed) {
+ return false;
+ }
+ }
+
+ //Do not process directories
+ if (String8(entry->getFileName()).size() == 0) {
+ return false;
+ }
+
+ return true;
}
+
status_t AaptAssets::filter(Bundle* bundle)
{
WeakResourceFilter reqFilter;
@@ -1530,7 +1539,7 @@ status_t AaptAssets::buildIncludedResources(Bundle* bundle)
printf("Including resources from package: %s\n", includes[i].string());
}
- if (!mIncludedAssets.addAssetPath(includes[i], NULL)) {
+ if (!mIncludedAssets.addAssetPath(includes[i], 0)) {
fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
includes[i].string());
return UNKNOWN_ERROR;
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 4fdc964..5b66e4e 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -27,6 +27,8 @@ using namespace android;
extern const char * const gDefaultIgnoreAssets;
extern const char * gUserIgnoreAssets;
+extern bool endsWith(const char* haystack, const char* needle);
+
bool valid_symbol_name(const String8& str);
class AaptAssets;
@@ -146,7 +148,7 @@ class AaptFile : public RefBase
{
public:
AaptFile(const String8& sourceFile, const AaptGroupEntry& groupEntry,
- const String8& resType)
+ const String8& resType, const String8& zipFile=String8(""))
: mGroupEntry(groupEntry)
, mResourceType(resType)
, mSourceFile(sourceFile)
@@ -154,9 +156,11 @@ public:
, mDataSize(0)
, mBufferSize(0)
, mCompression(ZipEntry::kCompressStored)
+ , mZipFile(zipFile)
{
//printf("new AaptFile created %s\n", (const char*)sourceFile);
}
+
virtual ~AaptFile() {
free(mData);
}
@@ -188,6 +192,12 @@ public:
// no compression is ZipEntry::kCompressStored.
int getCompressionMethod() const { return mCompression; }
void setCompressionMethod(int c) { mCompression = c; }
+
+ // ZIP support. In this case the sourceFile is the zip entry name
+ // and zipFile is the path to the zip File.
+ // example: sourceFile = drawable-hdpi/foo.png, zipFile = res.zip
+ const String8& getZipFile() const { return mZipFile; }
+
private:
friend class AaptGroup;
@@ -199,6 +209,7 @@ private:
size_t mDataSize;
size_t mBufferSize;
int mCompression;
+ String8 mZipFile;
};
/**
@@ -540,6 +551,8 @@ public:
void addGroupEntry(const AaptGroupEntry& entry) { mGroupEntries.add(entry); }
ssize_t slurpFromArgs(Bundle* bundle);
+ ssize_t slurpResourceZip(Bundle* bundle, ZipFile* zip, const char* fullZipPath);
+ bool isEntryValid(Bundle* bundle, ZipEntry* entry);
sp<AaptSymbols> getSymbolsFor(const String8& name);
@@ -593,7 +606,11 @@ private:
const bool overwrite=false);
ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
- ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
+
+
+ status_t addEntry(const String8& entryName, const String8& entryLeaf,
+ const String8& entryDirFull, const String8& entryDir,
+ const String8& zipFile, int compressionMethod);
status_t filter(Bundle* bundle);
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
index b12867a..fc42b8c 100644
--- a/tools/aapt/AaptConfig.cpp
+++ b/tools/aapt/AaptConfig.cpp
@@ -224,9 +224,20 @@ bool parse(const String8& str, ConfigDescription* out) {
success:
if (out != NULL) {
+#ifndef HAVE_ANDROID_OS
applyVersionForCompatibility(&config);
+#else
+ // Calling applyVersionForCompatibility when compiling a theme can cause
+ // the path to be changed by AAPT which results in the themed assets not being
+ // loaded. The only time (as of right now) that aapt is run on an android device
+ // is when it is being used for themes, so this should be the correct behavior
+ // in this case. If AAPT is ever used on an android device for some other reason,
+ // we will need to change this.
+ printf("AAPT is running on Android, skipping applyVersionForCompatibility");
+#endif
*out = config;
}
+
return true;
}
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index cbe7c5d..145eb64 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -1,5 +1,6 @@
//
// Copyright 2006 The Android Open Source Project
+// This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
//
// State bundle. Used to pass around stuff like command-line args.
//
@@ -49,7 +50,7 @@ public:
Bundle(void)
: mCmd(kCommandUnknown), mVerbose(false), mAndroidList(false),
mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false),
- mUpdate(false), mExtending(false),
+ mUpdate(false), mExtending(false), mExtendedPackageId(0),
mRequireLocalization(false), mPseudolocalize(NO_PSEUDOLOCALIZATION),
mWantUTF16(false), mValues(false), mIncludeMetaData(false),
mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL),
@@ -65,6 +66,8 @@ public:
mProduct(NULL), mUseCrunchCache(false), mErrorOnFailedInsert(false),
mErrorOnMissingConfigEntry(false), mOutputTextSymbols(NULL),
mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL),
+ mOutputResourcesApkFile(NULL),
+ mInternalZipPath(NULL), mInputAPKFile(NULL),
mBuildSharedLibrary(false),
mArgc(0), mArgv(NULL)
{}
@@ -94,6 +97,8 @@ public:
void setUpdate(bool val) { mUpdate = val; }
bool getExtending(void) const { return mExtending; }
void setExtending(bool val) { mExtending = val; }
+ int getExtendedPackageId(void) const { return mExtendedPackageId; }
+ void setExtendedPackageId(int val) { mExtendedPackageId = val; }
bool getRequireLocalization(void) const { return mRequireLocalization; }
void setRequireLocalization(bool val) { mRequireLocalization = val; }
short getPseudolocalize(void) const { return mPseudolocalize; }
@@ -109,6 +114,10 @@ public:
void setJunkPath(bool val) { mJunkPath = val; }
const char* getOutputAPKFile() const { return mOutputAPKFile; }
void setOutputAPKFile(const char* val) { mOutputAPKFile = val; }
+ const char* getOutputResApk() { return mOutputResourcesApkFile; }
+ const char* getInputAPKFile() { return mInputAPKFile; }
+ void setInputAPKFile(const char* val) { mInputAPKFile = val; }
+ void setOutputResApk(const char* val) { mOutputResourcesApkFile = val; }
const char* getManifestPackageNameOverride() const { return mManifestPackageNameOverride; }
void setManifestPackageNameOverride(const char * val) { mManifestPackageNameOverride = val; }
const char* getInstrumentationPackageNameOverride() const { return mInstrumentationPackageNameOverride; }
@@ -204,6 +213,8 @@ public:
void setSingleCrunchInputFile(const char* val) { mSingleCrunchInputFile = val; }
const char* getSingleCrunchOutputFile() const { return mSingleCrunchOutputFile; }
void setSingleCrunchOutputFile(const char* val) { mSingleCrunchOutputFile = val; }
+ void setInternalZipPath(const char* val) { mInternalZipPath = val; }
+ const char* getInternalZipPath() const { return mInternalZipPath; }
bool getBuildSharedLibrary() const { return mBuildSharedLibrary; }
void setBuildSharedLibrary(bool val) { mBuildSharedLibrary = val; }
void setNoVersionVectors(bool val) { mNoVersionVectors = val; }
@@ -275,6 +286,7 @@ private:
bool mMakePackageDirs;
bool mUpdate;
bool mExtending;
+ int mExtendedPackageId;
bool mRequireLocalization;
short mPseudolocalize;
bool mWantUTF16;
@@ -326,6 +338,9 @@ private:
const char* mOutputTextSymbols;
const char* mSingleCrunchInputFile;
const char* mSingleCrunchOutputFile;
+ const char* mOutputResourcesApkFile;
+ const char* mInternalZipPath;
+ const char* mInputAPKFile;
bool mBuildSharedLibrary;
android::String8 mPlatformVersionCode;
android::String8 mPlatformVersionName;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 8a0a39c..4c10868 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -2439,21 +2439,48 @@ int doPackage(Bundle* bundle)
goto bail;
}
- // Write the apk
- if (outputAPKFile) {
+ if (outputAPKFile || bundle->getOutputResApk()) {
// Gather all resources and add them to the APK Builder. The builder will then
// figure out which Split they belong in.
err = addResourcesToBuilder(assets, builder);
if (err != NO_ERROR) {
goto bail;
}
+ }
+
+ //Write the res apk
+ if (bundle->getOutputResApk()) {
+ const char* resPath = bundle->getOutputResApk();
+ char *endptr;
+ int resApk_fd = strtol(resPath, &endptr, 10);
+
+ if (*endptr == '\0') {
+ //OutputResDir was a file descriptor
+ //Assume there is only one set of assets, when we deal with actual split apks this may have to change
+ err = writeAPK(bundle, resApk_fd, builder->getBaseSplit(), true);
+ } else {
+ //Assume there is only one set of assets, when we deal with actual split apks this may have to change
+ err = writeAPK(bundle, String8(bundle->getOutputResApk()), builder->getBaseSplit(), true);
+ }
+
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: writing '%s' failed\n", resPath);
+ goto bail;
+ }
+ }
+
+ // Write the apk
+ if (outputAPKFile) {
+ if (err != NO_ERROR) {
+ goto bail;
+ }
const Vector<sp<ApkSplit> >& splits = builder->getSplits();
const size_t numSplits = splits.size();
for (size_t i = 0; i < numSplits; i++) {
const sp<ApkSplit>& split = splits[i];
String8 outputPath = buildApkName(String8(outputAPKFile), split);
- err = writeAPK(bundle, outputPath, split);
+ err = writeAPK(bundle, outputPath, split, false);
if (err != NO_ERROR) {
fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
goto bail;
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index e4738f5..528a960 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -27,6 +27,15 @@ png_write_aapt_file(png_structp png_ptr, png_bytep data, png_size_t length)
}
}
+static void
+png_read_mem_file(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ PngMemoryFile* pngFile = (PngMemoryFile*) png_get_io_ptr(png_ptr);
+ status_t err = pngFile->read(data, length);
+ if (err != NO_ERROR) {
+ png_error(png_ptr, "Read Error");
+ }
+}
static void
png_flush_aapt_file(png_structp /* png_ptr */)
@@ -1269,29 +1278,39 @@ static bool write_png_protected(png_structp write_ptr, String8& printableName, p
return true;
}
-status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets */,
+status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets */, //non-theme path
const sp<AaptFile>& file, String8* /* outNewLeafName */)
{
String8 ext(file->getPath().getPathExtension());
+ bool isImageInZip = !file->getZipFile().isEmpty();
// We currently only process PNG images.
if (strcmp(ext.string(), ".png") != 0) {
return NO_ERROR;
}
+ String8 printableName(file->getPrintableSource());
+
+ // We currently only process nine patch PNG images when building a theme apk.
+ Bundle* b = const_cast<Bundle*>(bundle);
+ if (!endsWith(printableName.string(), ".9.png") && b->getOutputResApk() != NULL) {
+ if (bundle->getVerbose()) {
+ printf("Skipping image: %s\n", file->getPrintableSource().string());
+ }
+ return NO_ERROR;
+ }
+
// Example of renaming a file:
//*outNewLeafName = file->getPath().getBasePath().getFileName();
//outNewLeafName->append(".nupng");
- String8 printableName(file->getPrintableSource());
-
if (bundle->getVerbose()) {
printf("Processing image: %s\n", printableName.string());
}
png_structp read_ptr = NULL;
png_infop read_info = NULL;
- FILE* fp;
+ FILE* fp = NULL;
image_info imageInfo;
@@ -1300,12 +1319,7 @@ status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets *
status_t error = UNKNOWN_ERROR;
- fp = fopen(file->getSourceFile().string(), "rb");
- if (fp == NULL) {
- fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
- goto bail;
- }
-
+ const size_t nameLen = file->getPath().length();
read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
(png_error_ptr)NULL);
if (!read_ptr) {
@@ -1317,8 +1331,47 @@ status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets *
goto bail;
}
- if (!read_png_protected(read_ptr, printableName, read_info, file, fp, &imageInfo)) {
- goto bail;
+ if (isImageInZip) {
+ PngMemoryFile* pmf = new PngMemoryFile();
+
+ ZipFile* zip = new ZipFile;
+ status_t err = zip->open(file->getZipFile(), ZipFile::kOpenReadOnly);
+ if (NO_ERROR != err) {
+ fprintf(stderr, "ERROR: Unable to open %s\n", file->getZipFile().string());
+ return err;
+ }
+
+ ZipEntry* entry = zip->getEntryByName(file->getSourceFile().string());
+ size_t len = entry->getUncompressedLen();
+ void* data = zip->uncompress(entry);
+ void* buf = file->editData(len);
+ memcpy(buf, data, len);
+ free(data);
+
+ pmf->setDataSource((const char*)file->getData(), file->getSize());
+ png_set_read_fn(read_ptr, pmf, png_read_mem_file);
+ read_png(printableName.string(), read_ptr, read_info, &imageInfo);
+ if (nameLen > 6) {
+ const char* name = file->getPath().string();
+ if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
+ if (do_9patch(printableName.string(), &imageInfo) != NO_ERROR) {
+ goto bail;
+ }
+ }
+ }
+ } else {
+ fp = fopen(file->getSourceFile().string(), "rb");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
+ goto bail;
+ }
+ if (!read_png_protected(read_ptr, printableName, read_info, file, fp, &imageInfo)) {
+ goto bail;
+ }
+ }
+
+ if (isImageInZip) {
+ file->clearData();
}
write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, (png_error_ptr)NULL,
@@ -1343,13 +1396,15 @@ status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& /* assets *
error = NO_ERROR;
- if (bundle->getVerbose()) {
+ if (bundle->getVerbose() && !isImageInZip) {
fseek(fp, 0, SEEK_END);
size_t oldSize = (size_t)ftell(fp);
size_t newSize = file->getSize();
float factor = ((float)newSize)/oldSize;
int percent = (int)(factor*100);
printf(" (processed image %s: %d%% size of source)\n", printableName.string(), percent);
+ } else if (bundle->getVerbose() && isImageInZip) {
+ printf(" (processed image %s)\n", printableName.string());
}
bail:
@@ -1511,3 +1566,17 @@ status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
return NO_ERROR;
}
+
+status_t PngMemoryFile::read(png_bytep data, png_size_t length) {
+ if (data == NULL)
+ return -1;
+
+ if ((mIndex + length) >= mDataSize) {
+ length = mDataSize - mIndex;
+ }
+
+ memcpy(data, mData + mIndex, length);
+ mIndex += length;
+
+ return NO_ERROR;
+}
diff --git a/tools/aapt/Images.h b/tools/aapt/Images.h
index a0a94f8..3230ddc 100644
--- a/tools/aapt/Images.h
+++ b/tools/aapt/Images.h
@@ -10,6 +10,8 @@
#include "ResourceTable.h"
#include "Bundle.h"
+#include <png.h>
+
#include <utils/String8.h>
#include <utils/RefBase.h>
@@ -23,4 +25,18 @@ status_t preProcessImageToCache(const Bundle* bundle, const String8& source, con
status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
ResourceTable* table, const sp<AaptFile>& file);
+class PngMemoryFile {
+public:
+ PngMemoryFile(void)
+ : mData(NULL), mDataSize(0), mIndex(0)
+ {}
+ void setDataSource(const char* data, uint32_t size) { mData = data; mDataSize = size; mIndex = 0; }
+ status_t read(png_bytep data, png_size_t length);
+
+private:
+ const char* mData;
+ uint32_t mDataSize;
+ uint32_t mIndex;
+};
+
#endif
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index f832c60..aa67480 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -1,5 +1,6 @@
//
// Copyright 2006 The Android Open Source Project
+// This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
//
// Android Asset Packaging Tool main entry point.
//
@@ -14,6 +15,7 @@
#include <cstdlib>
#include <getopt.h>
#include <cassert>
+#include <ctype.h>
using namespace android;
@@ -56,7 +58,7 @@ void usage(void)
" xmltree Print the compiled xmls in the given assets.\n"
" xmlstrings Print the strings of the given compiled xml assets.\n\n", gProgName);
fprintf(stderr,
- " %s p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml] \\\n"
+ " %s p[ackage] [-d][-f][-m][-u][-v][-x[ extending-resource-id]][-z][-M AndroidManifest.xml] \\\n"
" [-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile] \\\n"
" [--debug-mode] [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n"
" [--app-version VAL] [--app-version-name TEXT] [--custom-package VAL] \\\n"
@@ -114,7 +116,7 @@ void usage(void)
" -m make package directories under location specified by -J\n"
" -u update existing packages (add new, replace older, remove deleted files)\n"
" -v verbose output\n"
- " -x create extending (non-application) resource IDs\n"
+ " -x either create or assign (if specified) extending (non-application) resource IDs\n"
" -z require localization of resource attributes marked with\n"
" localization=\"suggested\"\n"
" -A additional directory in which to find raw asset files\n"
@@ -347,6 +349,14 @@ int main(int argc, char* const argv[])
break;
case 'x':
bundle.setExtending(true);
+ argc--;
+ argv++;
+ if (!argc || !isdigit(argv[0][0])) {
+ argc++;
+ argv--;
+ } else {
+ bundle.setExtendedPackageId(atoi(argv[0]));
+ }
break;
case 'z':
bundle.setRequireLocalization(true);
@@ -428,6 +438,17 @@ int main(int argc, char* const argv[])
convertPath(argv[0]);
bundle.setAndroidManifestFile(argv[0]);
break;
+ case 'X':
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '-X' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ convertPath(argv[0]);
+ bundle.setInternalZipPath(argv[0]);
+ break;
case 'P':
argc--;
argv++;
@@ -497,6 +518,28 @@ int main(int argc, char* const argv[])
bundle.setCompressionMethod(ZipEntry::kCompressStored);
}
break;
+ case 'Z':
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '-Z' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ convertPath(argv[0]);
+ bundle.setInputAPKFile(argv[0]);
+ break;
+ case 'r':
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '-r' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ convertPath(argv[0]);
+ bundle.setOutputResApk(argv[0]);
+ break;
case '-':
if (strcmp(cp, "-debug-mode") == 0) {
bundle.setDebugMode(true);
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index e84c4c5..0b8adbe 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -42,7 +42,14 @@ extern int calcPercent(long uncompressedLen, long compressedLen);
extern android::status_t writeAPK(Bundle* bundle,
const android::String8& outputFile,
- const android::sp<OutputSet>& outputSet);
+ const android::sp<OutputSet>& outputSet,
+ bool isOverlay);
+extern android::status_t writeAPK(Bundle* bundle,
+ int fd,
+ const android::sp<OutputSet>& outputSet,
+ bool isOverlay);
+extern android::status_t writeResFile(FILE* fp, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder);
+extern sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true);
extern android::status_t updatePreProcessedCache(Bundle* bundle);
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index cb244ec..3daf644 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -37,8 +37,10 @@ static const char* kNoCompressExt[] = {
};
/* fwd decls, so I can write this downward */
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet);
bool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet, bool isOverlay);
+bool processOverlayFile(Bundle* bundle, ZipFile* zip,
+ String8 storageName, const sp<const AaptFile>& file);
bool okayToCompress(Bundle* bundle, const String8& pathName);
ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
@@ -49,7 +51,81 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
* On success, "bundle->numPackages" will be the number of Zip packages
* we created.
*/
-status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet)
+status_t writeAPK(Bundle* bundle, ZipFile* zip, const char* outputFileName,
+ const sp<OutputSet>& outputSet, bool isOverlay)
+{
+ status_t result = NO_ERROR;
+ int count;
+
+ if (bundle->getVerbose()) {
+ printf("Writing all files...\n");
+ }
+
+ count = processAssets(bundle, zip, outputSet, isOverlay);
+ if (count < 0) {
+ fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
+ outputFileName);
+ result = count;
+ goto bail;
+ }
+
+ if (bundle->getVerbose()) {
+ printf("Generated %d file%s\n", count, (count==1) ? "" : "s");
+ }
+
+ if (!isOverlay) {
+ count = processJarFiles(bundle, zip);
+ if (count < 0) {
+ fprintf(stderr, "ERROR: unable to process jar files while packaging '%s'\n",
+ outputFileName);
+ result = count;
+ goto bail;
+ }
+
+ if (bundle->getVerbose())
+ printf("Included %d file%s from jar/zip files.\n", count, (count==1) ? "" : "s");
+ }
+
+ result = NO_ERROR;
+
+ /*
+ * Check for cruft. We set the "marked" flag on all entries we created
+ * or decided not to update. If the entry isn't already slated for
+ * deletion, remove it now.
+ */
+ {
+ if (bundle->getVerbose())
+ printf("Checking for deleted files\n");
+ int i, removed = 0;
+ for (i = 0; i < zip->getNumEntries(); i++) {
+ ZipEntry* entry = zip->getEntryByIndex(i);
+
+ if (!entry->getMarked() && entry->getDeleted()) {
+ if (bundle->getVerbose()) {
+ printf(" (removing crufty '%s')\n",
+ entry->getFileName());
+ }
+ zip->remove(entry);
+ removed++;
+ }
+ }
+ if (bundle->getVerbose() && removed > 0)
+ printf("Removed %d file%s\n", removed, (removed==1) ? "" : "s");
+ }
+
+ /* tell Zip lib to process deletions and other pending changes */
+ result = zip->flush();
+ if (result != NO_ERROR) {
+ fprintf(stderr, "ERROR: Zip flush failed, archive may be hosed\n");
+ goto bail;
+ }
+
+bail:
+ return result;
+}
+
+status_t writeAPK(Bundle* bundle, const String8& outputFile,
+ const sp<OutputSet>& outputSet, bool isOverlay)
{
#if BENCHMARK
fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
@@ -58,7 +134,6 @@ status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>
status_t result = NO_ERROR;
ZipFile* zip = NULL;
- int count;
//bundle->setPackageCount(0);
@@ -105,64 +180,10 @@ status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>
goto bail;
}
- if (bundle->getVerbose()) {
- printf("Writing all files...\n");
- }
-
- count = processAssets(bundle, zip, outputSet);
- if (count < 0) {
- fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
- outputFile.string());
- result = count;
- goto bail;
- }
-
- if (bundle->getVerbose()) {
- printf("Generated %d file%s\n", count, (count==1) ? "" : "s");
- }
-
- count = processJarFiles(bundle, zip);
- if (count < 0) {
- fprintf(stderr, "ERROR: unable to process jar files while packaging '%s'\n",
- outputFile.string());
- result = count;
- goto bail;
- }
-
- if (bundle->getVerbose())
- printf("Included %d file%s from jar/zip files.\n", count, (count==1) ? "" : "s");
-
- result = NO_ERROR;
+ result = writeAPK(bundle, zip, outputFile.string(), outputSet, isOverlay);
- /*
- * Check for cruft. We set the "marked" flag on all entries we created
- * or decided not to update. If the entry isn't already slated for
- * deletion, remove it now.
- */
- {
- if (bundle->getVerbose())
- printf("Checking for deleted files\n");
- int i, removed = 0;
- for (i = 0; i < zip->getNumEntries(); i++) {
- ZipEntry* entry = zip->getEntryByIndex(i);
-
- if (!entry->getMarked() && entry->getDeleted()) {
- if (bundle->getVerbose()) {
- printf(" (removing crufty '%s')\n",
- entry->getFileName());
- }
- zip->remove(entry);
- removed++;
- }
- }
- if (bundle->getVerbose() && removed > 0)
- printf("Removed %d file%s\n", removed, (removed==1) ? "" : "s");
- }
-
- /* tell Zip lib to process deletions and other pending changes */
- result = zip->flush();
if (result != NO_ERROR) {
- fprintf(stderr, "ERROR: Zip flush failed, archive may be hosed\n");
+ fprintf(stderr, "ERROR: Writing apk failed\n");
goto bail;
}
@@ -215,7 +236,98 @@ bail:
return result;
}
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet)
+/*
+ * The directory hierarchy looks like this:
+ * "outputDir" and "assetRoot" are existing directories.
+ *
+ * On success, "bundle->numPackages" will be the number of Zip packages
+ * we created.
+ */
+status_t writeAPK(Bundle* bundle, int fd, const sp<OutputSet>& outputSet, bool isOverlay)
+{
+ #if BENCHMARK
+ fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
+ long startAPKTime = clock();
+ #endif /* BENCHMARK */
+
+ status_t result = NO_ERROR;
+ ZipFile* zip = NULL;
+
+ status_t status;
+ zip = new ZipFile;
+ status = zip->openfd(fd, ZipFile::kOpenReadWrite);
+ if (status != NO_ERROR) {
+ fprintf(stderr, "ERROR: unable to open file as Zip file for writing\n");
+ result = PERMISSION_DENIED;
+ goto bail;
+ }
+
+ result = writeAPK(bundle, zip, "file_descriptor", outputSet, isOverlay);
+
+ if (result != NO_ERROR) {
+ fprintf(stderr, "ERROR: Writing apk failed\n");
+ goto bail;
+ }
+
+ /* anything here? */
+ if (zip->getNumEntries() == 0) {
+ if (bundle->getVerbose()) {
+ printf("Archive is empty -- removing\n");
+ }
+ delete zip; // close the file so we can remove it in Win32
+ zip = NULL;
+ close(fd);
+ }
+
+ assert(result == NO_ERROR);
+
+bail:
+ delete zip; // must close before remove in Win32
+ close(fd);
+ if (result != NO_ERROR) {
+ if (bundle->getVerbose()) {
+ printf("Removing archive due to earlier failures\n");
+ }
+ }
+
+ if (result == NO_ERROR && bundle->getVerbose())
+ printf("Done!\n");
+
+ #if BENCHMARK
+ fprintf(stdout, "BENCHMARK: End APK Bundling. Time Elapsed: %f ms \n",(clock() - startAPKTime)/1000.0);
+ #endif /* BENCHMARK */
+ return result;
+}
+
+status_t writeResFile(FILE* fp, const sp<AaptAssets>& /* assets */, sp<ApkBuilder>& builder) {
+ if (fp == NULL) {
+ fprintf(stderr, "Unable to open resFile for writing resTable\n");
+ return PERMISSION_DENIED;
+ }
+
+ sp<ApkSplit> split = builder->getBaseSplit();
+ const std::set<OutputEntry>& entries = split->getEntries();
+ std::set<OutputEntry>::const_iterator iter = entries.begin();
+ for (; iter != entries.end(); iter++) {
+ const OutputEntry& entry = *iter;
+
+ if (entry.getPath() == String8("resources.arsc")) {
+ sp<const AaptFile> resFile = entry.getFile();
+
+ int count = 0;
+ count = fwrite(resFile->getData(), 1, resFile->getSize(), fp);
+
+ if (count == 0) {
+ fprintf(stderr, "Nothing written to resFile\n");
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet,
+ bool isOverlay)
{
ssize_t count = 0;
const std::set<OutputEntry>& entries = outputSet->getEntries();
@@ -227,7 +339,9 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& o
} else {
String8 storagePath(entry.getPath());
storagePath.convertToResPath();
- if (!processFile(bundle, zip, storagePath, entry.getFile())) {
+ bool ret = isOverlay ? processOverlayFile(bundle, zip, storagePath, entry.getFile())
+ : processFile(bundle, zip, storagePath, entry.getFile());
+ if (!ret) {
return UNKNOWN_ERROR;
}
count++;
@@ -360,6 +474,76 @@ bool processFile(Bundle* bundle, ZipFile* zip,
}
/*
+ * Process a regular file, adding it to the archive if appropriate.
+ *
+ * This function is intended for use when creating a cached overlay package.
+ * Only xml and .9.png files are processed and added to the package.
+ *
+ * If we're in "update" mode, and the file already exists in the archive,
+ * delete the existing entry before adding the new one.
+ */
+bool processOverlayFile(Bundle* bundle, ZipFile* zip,
+ String8 storageName, const sp<const AaptFile>& file)
+{
+ const bool hasData = file->hasData();
+
+ storageName.convertToResPath();
+ ZipEntry* entry;
+ bool fromGzip = false;
+ status_t result;
+
+ if (strcasecmp(storageName.getPathExtension().string(), ".gz") == 0) {
+ fromGzip = true;
+ storageName = storageName.getBasePath();
+ }
+
+ if (bundle->getUpdate()) {
+ entry = zip->getEntryByName(storageName.string());
+ if (entry != NULL) {
+ /* file already exists in archive; there can be only one */
+ if (entry->getMarked()) {
+ fprintf(stderr,
+ "ERROR: '%s' exists twice (check for with & w/o '.gz'?)\n",
+ file->getPrintableSource().string());
+ return false;
+ }
+ zip->remove(entry);
+ }
+ }
+
+ if (hasData) {
+ const char* name = storageName.string();
+ if (endsWith(name, ".9.png") || endsWith(name, ".xml") || endsWith(name, ".arsc")) {
+ result = zip->add(file->getData(), file->getSize(), storageName.string(),
+ file->getCompressionMethod(), &entry);
+ if (result == NO_ERROR) {
+ if (bundle->getVerbose()) {
+ printf(" '%s'%s", storageName.string(), fromGzip ? " (from .gz)" : "");
+ if (entry->getCompressionMethod() == ZipEntry::kCompressStored) {
+ printf(" (not compressed)\n");
+ } else {
+ printf(" (compressed %d%%)\n", calcPercent(entry->getUncompressedLen(),
+ entry->getCompressedLen()));
+ }
+ }
+ entry->setMarked(true);
+ } else {
+ if (result == ALREADY_EXISTS) {
+ fprintf(stderr, " Unable to add '%s': file already in archive (try '-u'?)\n",
+ file->getPrintableSource().string());
+ } else {
+ fprintf(stderr, " Unable to add '%s': Zip add failed\n",
+ file->getPrintableSource().string());
+ }
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/*
* Determine whether or not we want to try to compress this file based
* on the file extension.
*/
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 5d20815..c636c28 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -221,6 +221,24 @@ bool isValidResourceType(const String8& type)
|| type == "color" || type == "menu" || type == "mipmap";
}
+sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary)
+{
+ sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
+ sp<AaptFile> file;
+ if (group != NULL) {
+ file = group->getFiles().valueFor(AaptGroupEntry());
+ if (file != NULL) {
+ return file;
+ }
+ }
+
+ if (!makeIfNecessary) {
+ return NULL;
+ }
+ return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
+ NULL, String8());
+}
+
static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
const sp<AaptGroup>& grp)
{
@@ -1170,7 +1188,9 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil
packageType = ResourceTable::AppFeature;
}
- ResourceTable table(bundle, String16(assets->getPackage()), packageType);
+ int extendedPackageId = bundle->getExtendedPackageId();
+
+ ResourceTable table(bundle, String16(assets->getPackage()), packageType, extendedPackageId);
err = table.addIncludedResources(bundle, assets);
if (err != NO_ERROR) {
return err;
@@ -1252,7 +1272,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil
bool hasErrors = false;
if (drawables != NULL) {
- if (bundle->getOutputAPKFile() != NULL) {
+ if (bundle->getOutputAPKFile() != NULL || bundle->getOutputResApk()) {
err = preProcessImages(bundle, assets, drawables, "drawable");
}
if (err == NO_ERROR) {
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 1f736d0..889883a 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1753,7 +1753,7 @@ status_t compileResourceFile(Bundle* bundle,
return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
}
-ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, ResourceTable::PackageType type)
+ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, ResourceTable::PackageType type, ssize_t pkgIdOverride)
: mAssetsPackage(assetsPackage)
, mPackageType(type)
, mTypeIdOffset(0)
@@ -1779,6 +1779,11 @@ ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, Reso
assert(0);
break;
}
+
+ if (pkgIdOverride != 0) {
+ packageId = pkgIdOverride;
+ }
+
sp<Package> package = new Package(mAssetsPackage, packageId);
mPackages.add(assetsPackage, package);
mOrderedPackages.add(package);
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index c4bdf09..2c2df19 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -109,7 +109,8 @@ public:
const ConfigDescription& sourceConfig,
const int sdkVersionToGenerate);
- ResourceTable(Bundle* bundle, const String16& assetsPackage, PackageType type);
+ ResourceTable(Bundle* bundle, const String16& assetsPackage, PackageType type,
+ ssize_t pkgIdOverride);
const String16& getAssetsPackage() const {
return mAssetsPackage;
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index ca3f687..6ab55d5 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -11,6 +11,7 @@
#include <utils/ByteOrder.h>
#include <errno.h>
#include <string.h>
+#include <androidfw/AssetManager.h>
#ifndef HAVE_MS_C_RUNTIME
#define O_BINARY 0
@@ -583,9 +584,51 @@ status_t parseXMLResource(const sp<AaptFile>& file, ResXMLTree* outTree,
return NO_ERROR;
}
+sp<XMLNode> XMLNode::parseFromZip(const sp<AaptFile>& file) {
+ AssetManager assets;
+ int32_t cookie;
+
+ if (!assets.addAssetPath(file->getZipFile(), &cookie)) {
+ fprintf(stderr, "Error: Could not open path %s\n", file->getZipFile().string());
+ return NULL;
+ }
+
+ Asset* asset = assets.openNonAsset(cookie, file->getSourceFile(), Asset::ACCESS_BUFFER);
+ ssize_t len = asset->getLength();
+ const void* buf = asset->getBuffer(false);
+
+ XML_Parser parser = XML_ParserCreateNS(NULL, 1);
+ ParseState state;
+ state.filename = file->getPrintableSource();
+ state.parser = parser;
+ XML_SetUserData(parser, &state);
+ XML_SetElementHandler(parser, startElement, endElement);
+ XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace);
+ XML_SetCharacterDataHandler(parser, characterData);
+ XML_SetCommentHandler(parser, commentData);
+
+ bool done = true;
+ if (XML_Parse(parser, (char*) buf, len, done) == XML_STATUS_ERROR) {
+ SourcePos(file->getSourceFile(), (int)XML_GetCurrentLineNumber(parser)).error(
+ "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser)));
+ return NULL;
+ }
+ XML_ParserFree(parser);
+ if (state.root == NULL) {
+ SourcePos(file->getSourceFile(), -1).error("No XML data generated when parsing");
+ }
+ return state.root;
+}
+
sp<XMLNode> XMLNode::parse(const sp<AaptFile>& file)
{
char buf[16384];
+
+ //Check for zip first
+ if (file->getZipFile().length() > 0) {
+ return parseFromZip(file);
+ }
+
int fd = open(file->getSourceFile().string(), O_RDONLY | O_BINARY);
if (fd < 0) {
SourcePos(file->getSourceFile(), -1).error("Unable to open file for read: %s",
diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h
index 3161f65..905c6fd 100644
--- a/tools/aapt/XMLNode.h
+++ b/tools/aapt/XMLNode.h
@@ -188,6 +188,9 @@ private:
status_t flatten_node(const StringPool& strings, const sp<AaptFile>& dest,
bool stripComments, bool stripRawValues) const;
+ static sp<XMLNode> parseFromZip(const sp<AaptFile>& file);
+ static sp<XMLNode> parseFromAsset(const Asset& asset);
+
String16 mNamespacePrefix;
String16 mNamespaceUri;
String16 mElementName;
diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp
index 36f4e73..8a4eef5 100644
--- a/tools/aapt/ZipFile.cpp
+++ b/tools/aapt/ZipFile.cpp
@@ -130,6 +130,71 @@ status_t ZipFile::open(const char* zipFileName, int flags)
}
/*
+ * Open a file and parse its guts.
+ */
+status_t ZipFile::openfd(int fd, int flags)
+{
+ bool newArchive = true;
+
+ assert(mZipFp == NULL); // no reopen
+
+ if ((flags & kOpenTruncate))
+ flags |= kOpenCreate; // trunc implies create
+
+ if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
+ return INVALID_OPERATION; // not both
+ if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
+ return INVALID_OPERATION; // not neither
+ if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
+ return INVALID_OPERATION; // create requires write
+
+ /* open the file */
+ const char* openflags;
+ if (flags & kOpenReadWrite) {
+ if (newArchive)
+ openflags = FILE_OPEN_RW_CREATE;
+ else
+ openflags = FILE_OPEN_RW;
+ } else {
+ openflags = FILE_OPEN_RO;
+ }
+ mZipFp = fdopen(fd, openflags);
+ if (mZipFp == NULL) {
+ int err = errno;
+ ALOGD("fdopen failed: %s\n", strerror(err));
+ return errnoToStatus(err);
+ }
+
+ status_t result;
+ if (!newArchive) {
+ /*
+ * Load the central directory. If that fails, then this probably
+ * isn't a Zip archive.
+ */
+ result = readCentralDir();
+ } else {
+ /*
+ * Newly-created. The EndOfCentralDir constructor actually
+ * sets everything to be the way we want it (all zeroes). We
+ * set mNeedCDRewrite so that we create *something* if the
+ * caller doesn't add any files. (We could also just unlink
+ * the file if it's brand new and nothing was added, but that's
+ * probably doing more than we really should -- the user might
+ * have a need for empty zip files.)
+ */
+ mNeedCDRewrite = true;
+ result = NO_ERROR;
+ }
+
+ if (flags & kOpenReadOnly)
+ mReadOnly = true;
+ else
+ assert(!mReadOnly);
+
+ return result;
+}
+
+/*
* Return the Nth entry in the archive.
*/
ZipEntry* ZipFile::getEntryByIndex(int idx) const
diff --git a/tools/aapt/ZipFile.h b/tools/aapt/ZipFile.h
index 7877550..d5abbf1 100644
--- a/tools/aapt/ZipFile.h
+++ b/tools/aapt/ZipFile.h
@@ -64,6 +64,7 @@ public:
kOpenTruncate = 0x08, // if it exists, empty it
};
status_t open(const char* zipFileName, int flags);
+ status_t openfd(int fd, int flags);
/*
* Add a file to the end of the archive. Specify whether you want the
diff --git a/tools/aapt/tests/ZipReading_test.cpp b/tools/aapt/tests/ZipReading_test.cpp
new file mode 100644
index 0000000..4b4f2da
--- /dev/null
+++ b/tools/aapt/tests/ZipReading_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <utils/KeyedVector.h>
+
+#include "mocks/MockZipFile.h"
+#include "mocks/MockZipEntry.h"
+
+#include "AaptConfig.h"
+#include "ConfigDescription.h"
+#include "TestHelper.h"
+
+#include "AaptAssets.h"
+
+using android::String8;
+using namespace testing;
+
+// A path to an apk that would be considered valid
+#define VALID_APK_FILE "/valid/valid.apk"
+
+// Internal zip path to a dir that aapt is being asked to compile
+#define COMPILING_OVERLAY_DIR "/assets/overlays/com.interesting.app"
+
+// Internal zip path to a valid resource. aapt is expected to compile this resource.
+#define COMPILING_OVERLAY_FILE COMPILING_OVERLAY_DIR "/res/drawable-xxhdpi/foo.png";
+
+// Internal zip path to another overlay dir that is NOT being compiled
+#define NOT_COMPILING_OVERLAY_DIR "/assets/overlays/com.boring.app"
+
+// Internal zip path to a resource for an overlay that is NOT compiling. aapt is expected to ignore
+#define NOT_COMPILING_OVERLAY_FILE COMPILING_OVERLAY_DIR "/assets/overlays/com.boring.app"
+
+static ::testing::AssertionResult TestParse(const String8& input, ConfigDescription* config=NULL) {
+ if (AaptConfig::parse(String8(input), config)) {
+ return ::testing::AssertionSuccess() << input << " was successfully parsed";
+ }
+ return ::testing::AssertionFailure() << input << " could not be parsed";
+}
+
+static ::testing::AssertionResult TestParse(const char* input, ConfigDescription* config=NULL) {
+ return TestParse(String8(input), config);
+}
+
+TEST(ZipReadingTest, TestValidZipEntryIsAdded) {
+ MockZipFile zip;
+ MockZipEntry entry1;
+ const char* zipFile = VALID_APK_FILE;
+ const char* validFilename = COMPILING_OVERLAY_FILE;
+
+ EXPECT_CALL(entry1, getFileName())
+ .WillRepeatedly(Return(validFilename));
+
+ EXPECT_CALL(zip, getNumEntries())
+ .Times(1)
+ .WillRepeatedly(Return(1));
+
+ EXPECT_CALL(zip, getEntryByIndex(_))
+ .Times(1)
+ .WillOnce(Return(&entry1));
+
+ sp<AaptAssets> assets = new AaptAssets();
+ Bundle bundle;
+ bundle.setInternalZipPath(COMPILING_OVERLAY_DIR);
+ ssize_t count = assets->slurpResourceZip(&bundle, &zip, zipFile);
+
+ Vector<sp<AaptDir> > dirs = assets->resDirs();
+ EXPECT_EQ(1, dirs.size());
+ EXPECT_EQ(1, count);
+}
+
+TEST(ZipReadingTest, TestDifferentThemeEntryNotAdded) {
+ MockZipFile zip;
+ MockZipEntry entry1;
+ const char* zipFile = VALID_APK_FILE;
+ const char* invalidFile = NOT_COMPILING_OVERLAY_FILE;
+
+ EXPECT_CALL(entry1, getFileName())
+ .WillRepeatedly(Return(invalidFile));
+
+ EXPECT_CALL(zip, getNumEntries())
+ .WillRepeatedly(Return(1));
+
+ EXPECT_CALL(zip, getEntryByIndex(_))
+ .Times(1)
+ .WillOnce(Return(&entry1));
+
+ sp<AaptAssets> assets = new AaptAssets();
+ Bundle bundle;
+ bundle.setInternalZipPath(COMPILING_OVERLAY_DIR);
+ ssize_t count = assets->slurpResourceZip(&bundle, &zip, zipFile);
+
+ Vector<sp<AaptDir> > dirs = assets->resDirs();
+ EXPECT_EQ(0, dirs.size());
+ EXPECT_EQ(0, count);
+}
+
+TEST(ZipReadingTest, TestOutsideEntryMarkedInvalid) {
+ Bundle bundle;
+ bundle.setInternalZipPath("VALID_OVERLAY_DIR");
+ MockZipEntry invalidEntry;
+ const char* invalidFile = NOT_COMPILING_OVERLAY_FILE;
+
+ EXPECT_CALL(invalidEntry, getFileName())
+ .WillRepeatedly(Return(invalidFile));
+
+ sp<AaptAssets> assets = new AaptAssets();
+ bool result = assets->isEntryValid(&bundle, &invalidEntry);
+
+ EXPECT_FALSE(result);
+}
+
+TEST(ZipReadingTest, TestNullEntryIsInvalid) {
+ Bundle bundle;
+ bundle.setInternalZipPath(COMPILING_OVERLAY_DIR);
+ MockZipEntry invalidEntry;
+ const char* invalidFile = NOT_COMPILING_OVERLAY_FILE;
+
+ EXPECT_CALL(invalidEntry, getFileName())
+ .WillRepeatedly(Return(invalidFile));
+
+ sp<AaptAssets> assets = new AaptAssets();
+ bool result = assets->isEntryValid(&bundle, NULL);
+
+ EXPECT_FALSE(result);
+}
+
+TEST(ZipReadingTest, TestDirectoryEntryMarkedInvalid) {
+ Bundle bundle;
+ bundle.setInternalZipPath(COMPILING_OVERLAY_DIR);
+ MockZipEntry invalidEntry2;
+ // Add a "/" signifying this is a dir entry not a file entry.
+ const char* dir2 = COMPILING_OVERLAY_DIR"/";
+ EXPECT_CALL(invalidEntry2, getFileName())
+ .WillRepeatedly(Return(dir2));
+
+ sp<AaptAssets> assets = new AaptAssets();
+ bool result2 = assets->isEntryValid(&bundle, &invalidEntry2);
+
+ EXPECT_FALSE(result2);
+}
diff --git a/tools/aapt/tests/mocks/MockZipEntry.h b/tools/aapt/tests/mocks/MockZipEntry.h
new file mode 100644
index 0000000..eef07f9
--- /dev/null
+++ b/tools/aapt/tests/mocks/MockZipEntry.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef ANDROID_MOCK_ZIP_ENTRY
+#define ANDROID_MOCK_ZIP_ENTRY
+
+#include "ZipFile.h"
+#include "gmock/gmock.h"
+
+class MockZipEntry : public android::ZipEntry {
+public:
+ MOCK_CONST_METHOD0(getCompressionMethod, int());
+ MOCK_CONST_METHOD0(getFileName, const char* ());
+};
+
+#endif // ANDROID_MOCK_ZIP_ENTRY
diff --git a/tools/aapt/tests/mocks/MockZipFile.h b/tools/aapt/tests/mocks/MockZipFile.h
new file mode 100644
index 0000000..0394ce7
--- /dev/null
+++ b/tools/aapt/tests/mocks/MockZipFile.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef ANDROID_MOCK_ZIP_FILE
+#define ANDROID_MOCK_ZIP_FILE
+
+#include "ZipFile.h"
+#include "gmock/gmock.h"
+
+class MockZipFile : public android::ZipFile {
+public:
+ MOCK_CONST_METHOD0(getNumEntries, int());
+ MOCK_CONST_METHOD1(getEntryByIndex, android::ZipEntry* (int idx));
+};
+
+#endif // ANDROID_MOCK_ZIP_FILE