From 6648ff7840a4819fe656f4c1a7f172fae4fe41a9 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Sun, 30 Aug 2009 13:36:22 -0700 Subject: [PATCH] Add a flag (-G) to aapt to have it output the classes that shouldn't be obfuscated by ProGuard. --- AaptAssets.cpp | 13 +++ AaptAssets.h | 1 + Bundle.h | 5 +- Command.cpp | 8 +- Main.cpp | 16 +++- Main.h | 5 ++ Resource.cpp | 227 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 271 insertions(+), 4 deletions(-) diff --git a/AaptAssets.cpp b/AaptAssets.cpp index dbcef6d..b00d8b0 100644 --- a/AaptAssets.cpp +++ b/AaptAssets.cpp @@ -1819,6 +1819,19 @@ void AaptAssets::print() const AaptDir::print(); } +sp AaptAssets::resDir(const String8& name) +{ + const Vector >& dirs = mDirs; + const size_t N = dirs.size(); + for (size_t i=0; i& d = dirs.itemAt(i); + if (d->getLeaf() == name) { + return d; + } + } + return NULL; +} + bool valid_symbol_name(const String8& symbol) { diff --git a/AaptAssets.h b/AaptAssets.h index 32efa4e..865efd1 100644 --- a/AaptAssets.h +++ b/AaptAssets.h @@ -508,6 +508,7 @@ public: void print() const; inline const Vector >& resDirs() { return mDirs; } + sp resDir(const String8& name); inline sp getOverlay() { return mOverlay; } inline void setOverlay(sp& overlay) { mOverlay = overlay; } diff --git a/Bundle.h b/Bundle.h index a671bd7..234e5b2 100644 --- a/Bundle.h +++ b/Bundle.h @@ -39,7 +39,7 @@ public: mRequireLocalization(false), mPseudolocalize(false), mValues(false), mCompressionMethod(0), mOutputAPKFile(NULL), - mAssetSourceDir(NULL), + mAssetSourceDir(NULL), mProguardFile(NULL), mAndroidManifestFile(NULL), mPublicOutputFile(NULL), mRClassDir(NULL), mResourceIntermediatesDir(NULL), mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL), @@ -88,6 +88,8 @@ public: */ const char* getAssetSourceDir() const { return mAssetSourceDir; } void setAssetSourceDir(const char* dir) { mAssetSourceDir = dir; } + const char* getProguardFile() const { return mProguardFile; } + void setProguardFile(const char* file) { mProguardFile = file; } const android::Vector& getResourceSourceDirs() const { return mResourceSourceDirs; } void addResourceSourceDir(const char* dir) { mResourceSourceDirs.insertAt(dir,0); } const char* getAndroidManifestFile() const { return mAndroidManifestFile; } @@ -161,6 +163,7 @@ private: int mCompressionMethod; const char* mOutputAPKFile; const char* mAssetSourceDir; + const char* mProguardFile; const char* mAndroidManifestFile; const char* mPublicOutputFile; const char* mRClassDir; diff --git a/Command.cpp b/Command.cpp index 790b474..f2cdf75 100644 --- a/Command.cpp +++ b/Command.cpp @@ -233,7 +233,7 @@ static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) return -1; } -static String8 getAttribute(const ResXMLTree& tree, const char* ns, +String8 getAttribute(const ResXMLTree& tree, const char* ns, const char* attr, String8* outError) { ssize_t idx = tree.indexOfAttribute(ns, attr); @@ -1158,6 +1158,12 @@ int doPackage(Bundle* bundle) } } + // Write out the ProGuard file + err = writeProguardFile(bundle, assets); + if (err < 0) { + goto bail; + } + // Write the apk if (outputAPKFile) { err = writeAPK(bundle, assets, String8(outputAPKFile)); diff --git a/Main.cpp b/Main.cpp index 882714c..e61010c 100644 --- a/Main.cpp +++ b/Main.cpp @@ -59,9 +59,9 @@ void usage(void) " [-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile] \\\n" " [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n" " [--max-sdk-version VAL] [--app-version VAL] \\\n" - " [--app-version-name TEXT] \\\n" + " [--app-version-name TEXT]\\\n" " [-I base-package [-I base-package ...]] \\\n" - " [-A asset-source-dir] [-P public-definitions-file] \\\n" + " [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n" " [-S resource-sources [-S resource-sources ...]] " " [-F apk-file] [-J R-file-dir] \\\n" " [raw-files-dir [raw-files-dir] ...]\n" @@ -109,6 +109,7 @@ void usage(void) " -z require localization of resource attributes marked with\n" " localization=\"suggested\"\n" " -A additional directory in which to find raw asset files\n" + " -G A file to output proguard options into.\n" " -F specify the apk file to output\n" " -I add an existing package to base include set\n" " -J specify where to output R.java resource constant definitions\n" @@ -274,6 +275,17 @@ int main(int argc, char* const argv[]) convertPath(argv[0]); bundle.setAssetSourceDir(argv[0]); break; + case 'G': + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '-G' option\n"); + wantUsage = true; + goto bail; + } + convertPath(argv[0]); + bundle.setProguardFile(argv[0]); + break; case 'I': argc--; argv++; diff --git a/Main.h b/Main.h index 34ca5e5..3ba4f39 100644 --- a/Main.h +++ b/Main.h @@ -33,6 +33,8 @@ extern android::status_t buildResources(Bundle* bundle, extern android::status_t writeResourceSymbols(Bundle* bundle, const sp& assets, const String8& pkgName, bool includePrivate); +extern android::status_t writeProguardFile(Bundle* bundle, const sp& assets); + extern bool isValidResourceType(const String8& type); ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp& assets); @@ -41,4 +43,7 @@ extern status_t filterResources(Bundle* bundle, const sp& assets); int dumpResources(Bundle* bundle); +String8 getAttribute(const ResXMLTree& tree, const char* ns, + const char* attr, String8* outError); + #endif // __MAIN_H diff --git a/Resource.cpp b/Resource.cpp index e8410cd..be05818 100644 --- a/Resource.cpp +++ b/Resource.cpp @@ -1630,3 +1630,230 @@ status_t writeResourceSymbols(Bundle* bundle, const sp& assets, return NO_ERROR; } + + + +class ProguardKeepSet +{ +public: + // { rule --> { file locations } } + KeyedVector > rules; + + void add(const String8& rule, const String8& where); +}; + +void ProguardKeepSet::add(const String8& rule, const String8& where) +{ + ssize_t index = rules.indexOfKey(rule); + if (index < 0) { + index = rules.add(rule, SortedVector()); + } + rules.editValueAt(index).add(where); +} + +status_t +writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp& assets) +{ + status_t err; + ResXMLTree tree; + size_t len; + ResXMLTree::event_code_t code; + int depth = 0; + bool inApplication = false; + String8 error; + sp assGroup; + sp assFile; + String8 pkg; + + // First, look for a package file to parse. This is required to + // be able to generate the resource information. + assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml")); + if (assGroup == NULL) { + fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); + return -1; + } + + if (assGroup->getFiles().size() != 1) { + fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", + assGroup->getFiles().valueAt(0)->getPrintableSource().string()); + } + + assFile = assGroup->getFiles().valueAt(0); + + err = parseXMLResource(assFile, &tree); + if (err != NO_ERROR) { + return err; + } + + tree.restart(); + + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code == ResXMLTree::END_TAG) { + if (/* name == "Application" && */ depth == 2) { + inApplication = false; + } + depth--; + continue; + } + if (code != ResXMLTree::START_TAG) { + continue; + } + depth++; + String8 tag(tree.getElementName(&len)); + // printf("Depth %d tag %s\n", depth, tag.string()); + if (depth == 1) { + if (tag != "manifest") { + fprintf(stderr, "ERROR: manifest does not start with tag\n"); + return -1; + } + pkg = getAttribute(tree, NULL, "package", NULL); + } else if (depth == 2 && tag == "application") { + inApplication = true; + } + if (depth == 3 && inApplication) { + if (tag == "application" || tag == "activity" || tag == "service" || tag == "receiver" + || tag == "provider") { + String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android", + "name", &error); + if (error != "") { + fprintf(stderr, "ERROR: %s\n", error.string()); + return -1; + } + // asdf --> package.asdf + // .asdf .a.b --> package.asdf package.a.b + // asdf.adsf --> asdf.asdf + String8 rule("-keep class "); + const char* p = name.string(); + const char* q = strchr(p, '.'); + if (p == q) { + rule += pkg; + rule += name; + } else if (q == NULL) { + rule += pkg; + rule += "."; + rule += name; + } else { + rule += name; + } + + String8 location = tag; + location += " "; + location += assFile->getSourceFile(); + char lineno[20]; + sprintf(lineno, ":%d", tree.getLineNumber()); + location += lineno; + + keep->add(rule, location); + } + } + } + + return NO_ERROR; +} + +status_t +writeProguardForLayout(ProguardKeepSet* keep, const sp& layoutFile) +{ + status_t err; + ResXMLTree tree; + size_t len; + ResXMLTree::event_code_t code; + + err = parseXMLResource(layoutFile, &tree); + if (err != NO_ERROR) { + return err; + } + + tree.restart(); + + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code != ResXMLTree::START_TAG) { + continue; + } + String8 tag(tree.getElementName(&len)); + + // If there is no '.', we'll assume that it's one of the built in names. + if (strchr(tag.string(), '.')) { + String8 rule("-keep class "); + rule += tag; + rule += " { (...); }"; + + String8 location("view "); + location += layoutFile->getSourceFile(); + char lineno[20]; + sprintf(lineno, ":%d", tree.getLineNumber()); + location += lineno; + + keep->add(rule, location); + } + } + + return NO_ERROR; +} + +status_t +writeProguardForLayouts(ProguardKeepSet* keep, const sp& assets) +{ + status_t err; + sp layout = assets->resDir(String8("layout")); + + if (layout != NULL) { + const KeyedVector > groups = layout->getFiles(); + const size_t N = groups.size(); + for (size_t i=0; i& group = groups.valueAt(i); + const DefaultKeyedVector >& files = group->getFiles(); + const size_t M = files.size(); + for (size_t j=0; j& assets) +{ + status_t err = -1; + + if (!bundle->getProguardFile()) { + return NO_ERROR; + } + + ProguardKeepSet keep; + + err = writeProguardForAndroidManifest(&keep, assets); + if (err < 0) { + return err; + } + + err = writeProguardForLayouts(&keep, assets); + if (err < 0) { + return err; + } + + FILE* fp = fopen(bundle->getProguardFile(), "w+"); + if (fp == NULL) { + fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", + bundle->getProguardFile(), strerror(errno)); + return UNKNOWN_ERROR; + } + + const KeyedVector >& rules = keep.rules; + const size_t N = rules.size(); + for (size_t i=0; i& locations = rules.valueAt(i); + const size_t M = locations.size(); + for (size_t j=0; j