From d6c86a89d348cf584c1fa1d7d6ba809ab9eb5e21 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Tue, 21 Jul 2009 11:16:54 -0700 Subject: [PATCH] donut snapshot --- AaptAssets.cpp | 50 +++++++++++- AaptAssets.h | 3 + Bundle.h | 23 ++++++ Command.cpp | 196 +++++++++++++++++++++++++++++++++++++++------- Main.cpp | 78 ++++++++++++++++-- Resource.cpp | 93 ++++++++++++++++++---- ResourceTable.cpp | 176 ++++++++++++++++++++++++++++++++++++----- ResourceTable.h | 12 ++- XMLNode.cpp | 61 +++++++++++++++ XMLNode.h | 10 +++ 10 files changed, 634 insertions(+), 68 deletions(-) diff --git a/AaptAssets.cpp b/AaptAssets.cpp index 6bc1ee6..67af116 100644 --- a/AaptAssets.cpp +++ b/AaptAssets.cpp @@ -187,6 +187,13 @@ AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value) return 0; } + // screen layout + if (getScreenLayoutName(part.string(), &config)) { + *axis = AXIS_SCREENLAYOUT; + *value = config.screenLayout; + return 0; + } + // version if (getVersionName(part.string(), &config)) { *axis = AXIS_VERSION; @@ -202,7 +209,7 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) { Vector parts; - String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, vers; + String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, layout, vers; const char *p = dir; const char *q; @@ -378,6 +385,18 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) //printf("not screen size: %s\n", part.string()); } + if (getScreenLayoutName(part.string())) { + layout = part; + + index++; + if (index == N) { + goto success; + } + part = parts[index]; + } else { + //printf("not screen layout: %s\n", part.string()); + } + if (getVersionName(part.string())) { vers = part; @@ -404,6 +423,7 @@ success: this->keyboard = key; this->navigation = nav; this->screenSize = size; + this->screenLayout = layout; this->version = vers; // what is this anyway? @@ -435,6 +455,8 @@ AaptGroupEntry::toString() const s += ","; s += screenSize; s += ","; + s += screenLayout; + s += ","; s += version; return s; } @@ -483,6 +505,10 @@ AaptGroupEntry::toDirName(const String8& resType) const s += "-"; s += screenSize; } + if (this->screenLayout != "") { + s += "-"; + s += screenLayout; + } if (this->version != "") { s += "-"; s += version; @@ -786,6 +812,26 @@ bool AaptGroupEntry::getScreenSizeName(const char* name, return true; } +bool AaptGroupEntry::getScreenLayoutName(const char* name, + ResTable_config* out) +{ + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = out->SCREENLAYOUT_ANY; + return true; + } else if (strcmp(name, "smallscreen") == 0) { + if (out) out->screenLayout = out->SCREENLAYOUT_SMALL; + return true; + } else if (strcmp(name, "normalscreen") == 0) { + if (out) out->screenLayout = out->SCREENLAYOUT_NORMAL; + return true; + } else if (strcmp(name, "largescreen") == 0) { + if (out) out->screenLayout = out->SCREENLAYOUT_LARGE; + return true; + } + + return false; +} + bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out) { @@ -828,6 +874,7 @@ int AaptGroupEntry::compare(const AaptGroupEntry& o) const if (v == 0) v = keyboard.compare(o.keyboard); if (v == 0) v = navigation.compare(o.navigation); if (v == 0) v = screenSize.compare(o.screenSize); + if (v == 0) v = screenLayout.compare(o.screenLayout); if (v == 0) v = version.compare(o.version); return v; } @@ -846,6 +893,7 @@ ResTable_config AaptGroupEntry::toParams() const getKeyboardName(keyboard.string(), ¶ms); getNavigationName(navigation.string(), ¶ms); getScreenSizeName(screenSize.string(), ¶ms); + getScreenLayoutName(screenLayout.string(), ¶ms); getVersionName(version.string(), ¶ms); return params; } diff --git a/AaptAssets.h b/AaptAssets.h index 01c8140..3b96412 100644 --- a/AaptAssets.h +++ b/AaptAssets.h @@ -37,6 +37,7 @@ enum { AXIS_KEYBOARD, AXIS_NAVIGATION, AXIS_SCREENSIZE, + AXIS_SCREENLAYOUT, AXIS_VERSION }; @@ -62,6 +63,7 @@ public: String8 keyboard; String8 navigation; String8 screenSize; + String8 screenLayout; String8 version; bool initFromDirName(const char* dir, String8* resType); @@ -78,6 +80,7 @@ public: static bool getKeyboardName(const char* name, ResTable_config* out = NULL); static bool getNavigationName(const char* name, ResTable_config* out = NULL); static bool getScreenSizeName(const char* name, ResTable_config* out = NULL); + static bool getScreenLayoutName(const char* name, ResTable_config* out = NULL); static bool getVersionName(const char* name, ResTable_config* out = NULL); int compare(const AaptGroupEntry& o) const; diff --git a/Bundle.h b/Bundle.h index 2d8471b..a6fedf3 100644 --- a/Bundle.h +++ b/Bundle.h @@ -34,10 +34,13 @@ public: mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false), mUpdate(false), mExtending(false), mRequireLocalization(false), mPseudolocalize(false), + mValues(false), mCompressionMethod(0), mOutputAPKFile(NULL), mAssetSourceDir(NULL), mAndroidManifestFile(NULL), mPublicOutputFile(NULL), mRClassDir(NULL), mResourceIntermediatesDir(NULL), + mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL), + mVersionCode(NULL), mVersionName(NULL), mArgc(0), mArgv(NULL) {} ~Bundle(void) {} @@ -70,6 +73,8 @@ public: void setRequireLocalization(bool val) { mRequireLocalization = val; } bool getPseudolocalize(void) const { return mPseudolocalize; } void setPseudolocalize(bool val) { mPseudolocalize = val; } + bool getValues(void) const { return mValues; } + void setValues(bool val) { mValues = val; } int getCompressionMethod(void) const { return mCompressionMethod; } void setCompressionMethod(int val) { mCompressionMethod = val; } const char* getOutputAPKFile() const { return mOutputAPKFile; } @@ -99,6 +104,17 @@ public: const android::Vector& getNoCompressExtensions() const { return mNoCompressExtensions; } void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); } + const char* getMinSdkVersion() const { return mMinSdkVersion; } + void setMinSdkVersion(const char* val) { mMinSdkVersion = val; } + const char* getTargetSdkVersion() const { return mTargetSdkVersion; } + void setTargetSdkVersion(const char* val) { mTargetSdkVersion = val; } + const char* getMaxSdkVersion() const { return mMaxSdkVersion; } + void setMaxSdkVersion(const char* val) { mMaxSdkVersion = val; } + const char* getVersionCode() const { return mVersionCode; } + void setVersionCode(const char* val) { mVersionCode = val; } + const char* getVersionName() const { return mVersionName; } + void setVersionName(const char* val) { mVersionName = val; } + /* * Set and get the file specification. * @@ -138,6 +154,7 @@ private: bool mExtending; bool mRequireLocalization; bool mPseudolocalize; + bool mValues; int mCompressionMethod; const char* mOutputAPKFile; const char* mAssetSourceDir; @@ -151,6 +168,12 @@ private: android::Vector mNoCompressExtensions; android::Vector mResourceSourceDirs; + const char* mMinSdkVersion; + const char* mTargetSdkVersion; + const char* mMaxSdkVersion; + const char* mVersionCode; + const char* mVersionName; + /* file specification */ int mArgc; char* const* mArgv; diff --git a/Command.cpp b/Command.cpp index 6f3461d..e04491d 100644 --- a/Command.cpp +++ b/Command.cpp @@ -196,7 +196,7 @@ int doList(Bundle* bundle) printf("\nNo resource table found.\n"); } else { printf("\nResource table:\n"); - res.print(); + res.print(false); } Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", @@ -268,17 +268,19 @@ static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* o return str ? String8(str, len) : String8(); } -static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) +static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, + String8* outError, int32_t defValue = -1) { ssize_t idx = indexOfAttribute(tree, attrRes); if (idx < 0) { - return -1; + return defValue; } Res_value value; if (tree.getAttributeValue(idx, &value) != NO_ERROR) { - if (value.dataType != Res_value::TYPE_INT_DEC) { + if (value.dataType < Res_value::TYPE_FIRST_INT + || value.dataType > Res_value::TYPE_LAST_INT) { if (outError != NULL) *outError = "attribute is not an integer value"; - return -1; + return defValue; } } return value.data; @@ -318,7 +320,18 @@ enum { VERSION_NAME_ATTR = 0x0101021c, LABEL_ATTR = 0x01010001, ICON_ATTR = 0x01010002, - MIN_SDK_VERSION_ATTR = 0x0101020c + MIN_SDK_VERSION_ATTR = 0x0101020c, + REQ_TOUCH_SCREEN_ATTR = 0x01010227, + REQ_KEYBOARD_TYPE_ATTR = 0x01010228, + REQ_HARD_KEYBOARD_ATTR = 0x01010229, + REQ_NAVIGATION_ATTR = 0x0101022a, + REQ_FIVE_WAY_NAV_ATTR = 0x01010232, + TARGET_SDK_VERSION_ATTR = 0x01010270, + TEST_ONLY_ATTR = 0x01010272, + DENSITY_ATTR = 0x0101026c, + SMALL_SCREEN_ATTR = 0x01010284, + NORMAL_SCREEN_ATTR = 0x01010285, + LARGE_SCREEN_ATTR = 0x01010286, }; const char *getComponentName(String8 &pkgName, String8 &componentName) { @@ -357,7 +370,8 @@ int doDump(Bundle* bundle) const char* filename = bundle->getFileSpecEntry(1); AssetManager assets; - if (!assets.addAssetPath(String8(filename), NULL)) { + void* assetsCookie; + if (!assets.addAssetPath(String8(filename), &assetsCookie)) { fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n"); return 1; } @@ -369,7 +383,7 @@ int doDump(Bundle* bundle) } if (strcmp("resources", option) == 0) { - res.print(); + res.print(bundle->getValues()); } else if (strcmp("xmltree", option) == 0) { if (bundle->getFileSpecCount() < 3) { @@ -488,6 +502,10 @@ int doDump(Bundle* bundle) bool isLauncherActivity = false; bool withinApplication = false; bool withinReceiver = false; + int targetSdk = 0; + int smallScreen = 1; + int normalScreen = 1; + int largeScreen = 1; String8 pkg; String8 activityName; String8 activityLabel; @@ -543,15 +561,90 @@ int doDump(Bundle* bundle) goto bail; } printf("icon='%s'\n", icon.string()); - } else if (tag == "uses-sdk") { - int32_t sdkVersion = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); + int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); if (error != "") { - fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); goto bail; } - if (sdkVersion != -1) { - printf("sdkVersion:'%d'\n", sdkVersion); + if (testOnly != 0) { + printf("testOnly='%d'\n", testOnly); + } + } else if (tag == "uses-sdk") { + int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); + if (error != "") { + error = ""; + String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", + error.string()); + goto bail; + } + if (name == "Donut") targetSdk = 4; + printf("sdkVersion:'%s'\n", name.string()); + } else if (code != -1) { + targetSdk = code; + printf("sdkVersion:'%d'\n", code); + } + code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); + if (error != "") { + error = ""; + String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", + error.string()); + goto bail; + } + if (name == "Donut" && targetSdk < 4) targetSdk = 4; + printf("targetSdkVersion:'%s'\n", name.string()); + } else if (code != -1) { + if (targetSdk < code) { + targetSdk = code; + } + printf("targetSdkVersion:'%d'\n", code); + } + } else if (tag == "uses-configuration") { + int32_t reqTouchScreen = getIntegerAttribute(tree, + REQ_TOUCH_SCREEN_ATTR, NULL, 0); + int32_t reqKeyboardType = getIntegerAttribute(tree, + REQ_KEYBOARD_TYPE_ATTR, NULL, 0); + int32_t reqHardKeyboard = getIntegerAttribute(tree, + REQ_HARD_KEYBOARD_ATTR, NULL, 0); + int32_t reqNavigation = getIntegerAttribute(tree, + REQ_NAVIGATION_ATTR, NULL, 0); + int32_t reqFiveWayNav = getIntegerAttribute(tree, + REQ_FIVE_WAY_NAV_ATTR, NULL, 0); + printf("uses-configuation:"); + if (reqTouchScreen != 0) { + printf(" reqTouchScreen='%d'", reqTouchScreen); + } + if (reqKeyboardType != 0) { + printf(" reqKeyboardType='%d'", reqKeyboardType); + } + if (reqHardKeyboard != 0) { + printf(" reqHardKeyboard='%d'", reqHardKeyboard); + } + if (reqNavigation != 0) { + printf(" reqNavigation='%d'", reqNavigation); + } + if (reqFiveWayNav != 0) { + printf(" reqFiveWayNav='%d'", reqFiveWayNav); + } + printf("\n"); + } else if (tag == "supports-density") { + int32_t dens = getIntegerAttribute(tree, DENSITY_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:density' attribute: %s\n", + error.string()); + goto bail; } + printf("supports-density:'%d'\n", dens); + } else if (tag == "supports-screens") { + smallScreen = getIntegerAttribute(tree, + SMALL_SCREEN_ATTR, NULL, 1); + normalScreen = getIntegerAttribute(tree, + NORMAL_SCREEN_ATTR, NULL, 1); + largeScreen = getIntegerAttribute(tree, + LARGE_SCREEN_ATTR, NULL, 1); } } else if (depth == 3 && withinApplication) { withinActivity = false; @@ -592,18 +685,18 @@ int doDump(Bundle* bundle) } } } else if (depth == 5) { - if (withinActivity) { - if (tag == "action") { - //printf("LOG: action tag\n"); - String8 action = getAttribute(tree, NAME_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); - goto bail; - } - if (action == "android.intent.action.MAIN") { - isMainActivity = true; - //printf("LOG: isMainActivity==true\n"); - } + if (withinActivity) { + if (tag == "action") { + //printf("LOG: action tag\n"); + String8 action = getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); + goto bail; + } + if (action == "android.intent.action.MAIN") { + isMainActivity = true; + //printf("LOG: isMainActivity==true\n"); + } } else if (tag == "category") { String8 category = getAttribute(tree, NAME_ATTR, &error); if (error != "") { @@ -659,11 +752,31 @@ int doDump(Bundle* bundle) activityIcon.string()); } } + + // Determine default values for any unspecified screen sizes, + // based on the target SDK of the package. As of 4 (donut) + // the screen size support was introduced, so all default to + // enabled. + if (smallScreen > 0) { + smallScreen = targetSdk >= 4 ? -1 : 0; + } + if (normalScreen > 0) { + normalScreen = -1; + } + if (largeScreen > 0) { + largeScreen = targetSdk >= 4 ? -1 : 0; + } + printf("supports-screens:"); + if (smallScreen != 0) printf(" 'small'"); + if (normalScreen != 0) printf(" 'normal'"); + if (largeScreen != 0) printf(" 'large'"); + printf("\n"); + printf("locales:"); Vector locales; res.getLocales(&locales); - const size_t N = locales.size(); - for (size_t i=0; i configs; + res.getConfigurations(&configs); + SortedVector densities; + const size_t NC = configs.size(); + for (size_t i=0; igetFileCount() > 0) { + printf("native-code:"); + for (size_t i=0; igetFileCount(); i++) { + printf(" '%s'", dir->getFileName(i).string()); + } + printf("\n"); + } + delete dir; + } } else if (strcmp("configurations", option) == 0) { Vector configs; res.getConfigurations(&configs); diff --git a/Main.cpp b/Main.cpp index 71b1a3c..12a0445 100644 --- a/Main.cpp +++ b/Main.cpp @@ -45,7 +45,7 @@ void usage(void) " %s l[ist] [-v] [-a] file.{zip,jar,apk}\n" " List contents of Zip-compatible archive.\n\n", gProgName); fprintf(stderr, - " %s d[ump] WHAT file.{apk} [asset [asset ...]]\n" + " %s d[ump] [--values] WHAT file.{apk} [asset [asset ...]]\n" " badging Print the label and icon for the app declared in APK.\n" " permissions Print the permissions from the APK.\n" " resources Print the resource table from the APK.\n" @@ -54,9 +54,10 @@ void usage(void) " 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" - " [-0 extension [-0 extension ...]] \\\n" - " [-g tolerance] \\\n" - " [-j jarfile] \\\n" + " [-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" " [-I base-package [-I base-package ...]] \\\n" " [-A asset-source-dir] [-P public-definitions-file] \\\n" " [-S resource-sources [-S resource-sources ...]] " @@ -115,7 +116,19 @@ void usage(void) " and the first match found (left to right) will take precedence." " -0 specifies an additional extension for which such files will not\n" " be stored compressed in the .apk. An empty string means to not\n" - " compress any files at all.\n"); + " compress any files at all.\n" + " --min-sdk-version\n" + " inserts android:minSdkVersion in to manifest.\n" + " --target-sdk-version\n" + " inserts android:targetSdkVersion in to manifest.\n" + " --max-sdk-version\n" + " inserts android:maxSdkVersion in to manifest.\n" + " --values\n" + " when used with \"dump resources\" also includes resource values.\n" + " --version-code\n" + " inserts android:versionCode in to manifest.\n" + " --version-name\n" + " inserts android:versionName in to manifest.\n"); } /* @@ -339,6 +352,61 @@ int main(int argc, char* const argv[]) bundle.setCompressionMethod(ZipEntry::kCompressStored); } break; + case '-': + if (strcmp(cp, "-min-sdk-version") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--min-sdk-version' option\n"); + wantUsage = true; + goto bail; + } + bundle.setMinSdkVersion(argv[0]); + } else if (strcmp(cp, "-target-sdk-version") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--target-sdk-version' option\n"); + wantUsage = true; + goto bail; + } + bundle.setTargetSdkVersion(argv[0]); + } else if (strcmp(cp, "-max-sdk-version") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--max-sdk-version' option\n"); + wantUsage = true; + goto bail; + } + bundle.setMaxSdkVersion(argv[0]); + } else if (strcmp(cp, "-version-code") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--version-code' option\n"); + wantUsage = true; + goto bail; + } + bundle.setVersionCode(argv[0]); + } else if (strcmp(cp, "-version-name") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--version-name' option\n"); + wantUsage = true; + goto bail; + } + bundle.setVersionName(argv[0]); + } else if (strcmp(cp, "-values") == 0) { + bundle.setValues(true); + } else { + fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp); + wantUsage = true; + goto bail; + } + cp += strlen(cp) - 1; + break; default: fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp); wantUsage = true; diff --git a/Resource.cpp b/Resource.cpp index b2bd9ff..027e3ab 100644 --- a/Resource.cpp +++ b/Resource.cpp @@ -433,7 +433,7 @@ static void checkForIds(const String8& path, ResXMLParser& parser) } } -static void applyFileOverlay(const sp& assets, +static bool applyFileOverlay(const sp& assets, const sp& baseSet, const char *resType) { @@ -441,7 +441,7 @@ static void applyFileOverlay(const sp& assets, // Also add any found only in the overlay. sp overlay = assets->getOverlay(); String8 resTypeString(resType); - + // work through the linked list of overlays while (overlay.get()) { KeyedVector >* overlayRes = overlay->getResources(); @@ -456,7 +456,7 @@ static void applyFileOverlay(const sp& assets, size_t overlayCount = overlaySet->size(); for (size_t overlayIndex=0; overlayIndexindexOfKey(overlaySet->keyAt(overlayIndex)); - if (baseIndex != UNKNOWN_ERROR) { + if (baseIndex < UNKNOWN_ERROR) { // look for same flavor. For a given file (strings.xml, for example) // there may be a locale specific or other flavors - we want to match // the same flavor. @@ -482,9 +482,10 @@ static void applyFileOverlay(const sp& assets, } } else { // this group doesn't exist (a file that's only in the overlay) - // add it - baseSet->add(overlaySet->keyAt(overlayIndex), - overlaySet->valueAt(overlayIndex)); + fprintf(stderr, "aapt: error: " + "*** Resource file '%s' exists only in an overlay\n", + overlaySet->keyAt(overlayIndex).string()); + return false; } } // this overlay didn't have resources for this type @@ -492,7 +493,59 @@ static void applyFileOverlay(const sp& assets, // try next overlay overlay = overlay->getOverlay(); } - return; + return true; +} + +void addTagAttribute(const sp& node, const char* ns8, + const char* attr8, const char* value) +{ + if (value == NULL) { + return; + } + + const String16 ns(ns8); + const String16 attr(attr8); + + if (node->getAttribute(ns, attr) != NULL) { + fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s)\n", + String8(attr).string(), String8(ns).string()); + return; + } + + node->addAttribute(ns, attr, String16(value)); +} + +status_t massageManifest(Bundle* bundle, sp root) +{ + root = root->searchElement(String16(), String16("manifest")); + if (root == NULL) { + fprintf(stderr, "No tag.\n"); + return UNKNOWN_ERROR; + } + + addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", + bundle->getVersionCode()); + addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", + bundle->getVersionName()); + + if (bundle->getMinSdkVersion() != NULL + || bundle->getTargetSdkVersion() != NULL + || bundle->getMaxSdkVersion() != NULL) { + sp vers = root->getChildElement(String16(), String16("uses-sdk")); + if (vers == NULL) { + vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); + root->insertChildAt(vers, 0); + } + + addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", + bundle->getMinSdkVersion()); + addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion", + bundle->getTargetSdkVersion()); + addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion", + bundle->getMaxSdkVersion()); + } + + return NO_ERROR; } #define ASSIGN_IT(n) \ @@ -566,13 +619,15 @@ status_t buildResources(Bundle* bundle, const sp& assets) current = current->getOverlay(); } // apply the overlay files to the base set - applyFileOverlay(assets, drawables, "drawable"); - applyFileOverlay(assets, layouts, "layout"); - applyFileOverlay(assets, anims, "anim"); - applyFileOverlay(assets, xmls, "xml"); - applyFileOverlay(assets, raws, "raw"); - applyFileOverlay(assets, colors, "color"); - applyFileOverlay(assets, menus, "menu"); + if (!applyFileOverlay(assets, drawables, "drawable") || + !applyFileOverlay(assets, layouts, "layout") || + !applyFileOverlay(assets, anims, "anim") || + !applyFileOverlay(assets, xmls, "xml") || + !applyFileOverlay(assets, raws, "raw") || + !applyFileOverlay(assets, colors, "color") || + !applyFileOverlay(assets, menus, "menu")) { + return UNKNOWN_ERROR; + } bool hasErrors = false; @@ -1013,7 +1068,15 @@ status_t buildResources(Bundle* bundle, const sp& assets) // Generate final compiled manifest file. manifestFile->clearData(); - err = compileXmlFile(assets, manifestFile, &table); + sp manifestTree = XMLNode::parse(manifestFile); + if (manifestTree == NULL) { + return UNKNOWN_ERROR; + } + err = massageManifest(bundle, manifestTree); + if (err < NO_ERROR) { + return err; + } + err = compileXmlFile(assets, manifestTree, manifestFile, &table); if (err < NO_ERROR) { return err; } diff --git a/ResourceTable.cpp b/ResourceTable.cpp index ef11a83..b004664 100644 --- a/ResourceTable.cpp +++ b/ResourceTable.cpp @@ -23,6 +23,16 @@ status_t compileXmlFile(const sp& assets, if (root == NULL) { return UNKNOWN_ERROR; } + + return compileXmlFile(assets, root, target, table, options); +} + +status_t compileXmlFile(const sp& assets, + const sp& root, + const sp& target, + ResourceTable* table, + int options) +{ if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) { root->removeWhitespace(true, NULL); } else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) { @@ -651,6 +661,7 @@ status_t compileResourceFile(Bundle* bundle, const String16 string_array16("string-array"); const String16 integer_array16("integer-array"); const String16 public16("public"); + const String16 public_padding16("public-padding"); const String16 private_symbols16("private-symbols"); const String16 skip16("skip"); const String16 eat_comment16("eat-comment"); @@ -685,7 +696,7 @@ status_t compileResourceFile(Bundle* bundle, bool hasErrors = false; - uint32_t nextPublicId = 0; + DefaultKeyedVector nextPublicId(0); ResXMLTree::event_code_t code; do { @@ -718,6 +729,7 @@ status_t compileResourceFile(Bundle* bundle, String16 curType; int32_t curFormat = ResTable_map::TYPE_ANY; bool curIsBag = false; + bool curIsBagReplaceOnOverwrite = false; bool curIsStyled = false; bool curIsPseudolocalizable = false; bool localHasErrors = false; @@ -774,15 +786,15 @@ status_t compileResourceFile(Bundle* bundle, hasErrors = localHasErrors = true; } else { ident = identValue.data; - nextPublicId = ident+1; + nextPublicId.replaceValueFor(type, ident+1); } - } else if (nextPublicId == 0) { + } else if (nextPublicId.indexOfKey(type) < 0) { srcPos.error("No 'id' attribute supplied ," " and no previous id defined in this file.\n"); hasErrors = localHasErrors = true; } else if (!localHasErrors) { - ident = nextPublicId; - nextPublicId++; + ident = nextPublicId.valueFor(type); + nextPublicId.replaceValueFor(type, ident+1); } if (!localHasErrors) { @@ -816,6 +828,116 @@ status_t compileResourceFile(Bundle* bundle, } continue; + } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { + SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); + + String16 type; + ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); + if (typeIdx < 0) { + srcPos.error("A 'type' attribute is required for \n"); + hasErrors = localHasErrors = true; + } + type = String16(block.getAttributeStringValue(typeIdx, &len)); + + String16 name; + ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); + if (nameIdx < 0) { + srcPos.error("A 'name' attribute is required for \n"); + hasErrors = localHasErrors = true; + } + name = String16(block.getAttributeStringValue(nameIdx, &len)); + + uint32_t start = 0; + ssize_t startIdx = block.indexOfAttribute(NULL, "start"); + if (startIdx >= 0) { + const char16_t* startStr = block.getAttributeStringValue(startIdx, &len); + Res_value startValue; + if (!ResTable::stringToInt(startStr, len, &startValue)) { + srcPos.error("Given 'start' attribute is not an integer: %s\n", + String8(block.getAttributeStringValue(startIdx, &len)).string()); + hasErrors = localHasErrors = true; + } else { + start = startValue.data; + } + } else if (nextPublicId.indexOfKey(type) < 0) { + srcPos.error("No 'start' attribute supplied ," + " and no previous id defined in this file.\n"); + hasErrors = localHasErrors = true; + } else if (!localHasErrors) { + start = nextPublicId.valueFor(type); + } + + uint32_t end = 0; + ssize_t endIdx = block.indexOfAttribute(NULL, "end"); + if (endIdx >= 0) { + const char16_t* endStr = block.getAttributeStringValue(endIdx, &len); + Res_value endValue; + if (!ResTable::stringToInt(endStr, len, &endValue)) { + srcPos.error("Given 'end' attribute is not an integer: %s\n", + String8(block.getAttributeStringValue(endIdx, &len)).string()); + hasErrors = localHasErrors = true; + } else { + end = endValue.data; + } + } else { + srcPos.error("No 'end' attribute supplied \n"); + hasErrors = localHasErrors = true; + } + + if (end >= start) { + nextPublicId.replaceValueFor(type, end+1); + } else { + srcPos.error("Padding start '%ul' is after end '%ul'\n", + start, end); + hasErrors = localHasErrors = true; + } + + String16 comment( + block.getComment(&len) ? block.getComment(&len) : nulStr); + for (uint32_t curIdent=start; curIdent<=end; curIdent++) { + if (localHasErrors) { + break; + } + String16 curName(name); + char buf[64]; + sprintf(buf, "%d", (int)(end-curIdent+1)); + curName.append(String16(buf)); + + err = outTable->addEntry(srcPos, myPackage, type, curName, + String16("padding"), NULL, &curParams, false, + ResTable_map::TYPE_STRING, overwrite); + if (err < NO_ERROR) { + hasErrors = localHasErrors = true; + break; + } + err = outTable->addPublic(srcPos, myPackage, type, + curName, curIdent); + if (err < NO_ERROR) { + hasErrors = localHasErrors = true; + break; + } + sp symbols = assets->getSymbolsFor(String8("R")); + if (symbols != NULL) { + symbols = symbols->addNestedSymbol(String8(type), srcPos); + } + if (symbols != NULL) { + symbols->makeSymbolPublic(String8(curName), srcPos); + symbols->appendComment(String8(curName), comment, srcPos); + } else { + srcPos.error("Unable to create symbols!\n"); + hasErrors = localHasErrors = true; + } + } + + while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code == ResXMLTree::END_TAG) { + if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { + break; + } + } + } + continue; + } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) { String16 pkg; ssize_t pkgIdx = block.indexOfAttribute(NULL, "package"); @@ -1050,6 +1172,7 @@ status_t compileResourceFile(Bundle* bundle, curTag = &array16; curType = array16; curIsBag = true; + curIsBagReplaceOnOverwrite = true; ssize_t formatIdx = block.indexOfAttribute(NULL, "format"); if (formatIdx >= 0) { String16 formatStr = String16(block.getAttributeStringValue( @@ -1068,12 +1191,14 @@ status_t compileResourceFile(Bundle* bundle, curType = array16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; curIsBag = true; + curIsBagReplaceOnOverwrite = true; curIsPseudolocalizable = true; } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) { curTag = &integer_array16; curType = array16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER; curIsBag = true; + curIsBagReplaceOnOverwrite = true; } else { SourcePos(in->getPrintableSource(), block.getLineNumber()).error( "Found tag %s where item is expected\n", @@ -1108,9 +1233,10 @@ status_t compileResourceFile(Bundle* bundle, } if (!localHasErrors) { - err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()), - myPackage, curType, ident, parentIdent, &curParams, - overwrite); + err = outTable->startBag(SourcePos(in->getPrintableSource(), + block.getLineNumber()), myPackage, curType, ident, + parentIdent, &curParams, + overwrite, curIsBagReplaceOnOverwrite); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1307,7 +1433,7 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp e = getEntry(package, type, name, sourcePos, params, doSetIndex); + + sp e = getEntry(package, type, name, sourcePos, overwrite, + params, doSetIndex); if (e == NULL) { return UNKNOWN_ERROR; } @@ -1408,6 +1535,7 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, const String16& name, const String16& bagParent, const ResTable_config* params, + bool overlay, bool replace, bool isId) { status_t result = NO_ERROR; @@ -1428,8 +1556,12 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, sourcePos.file.striing(), sourcePos.line, String8(type).string()); } #endif - - sp e = getEntry(package, type, name, sourcePos, params); + if (overlay && !hasBagOrEntry(package, type, name)) { + sourcePos.error("Can't add new bags in an overlay. See '%s'\n", + String8(name).string()); + return UNKNOWN_ERROR; + } + sp e = getEntry(package, type, name, sourcePos, overlay, params); if (e == NULL) { return UNKNOWN_ERROR; } @@ -1450,7 +1582,7 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos, return result; } - if (replace) { + if (overlay && replace) { return e->emptyBag(sourcePos); } return result; @@ -1483,8 +1615,7 @@ status_t ResourceTable::addBag(const SourcePos& sourcePos, sourcePos.file.striing(), sourcePos.line, String8(type).string()); } #endif - - sp e = getEntry(package, type, name, sourcePos, params); + sp e = getEntry(package, type, name, sourcePos, replace, params); if (e == NULL) { return UNKNOWN_ERROR; } @@ -2767,7 +2898,7 @@ status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos, mItem.sourcePos.file.string(), mItem.sourcePos.line); return UNKNOWN_ERROR; } - + mType = TYPE_ITEM; mItem = item; mItemFormat = format; @@ -3087,11 +3218,17 @@ status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos, sp ResourceTable::Type::getEntry(const String16& entry, const SourcePos& sourcePos, const ResTable_config* config, - bool doSetIndex) + bool doSetIndex, + bool overlay) { int pos = -1; sp c = mConfigs.valueFor(entry); if (c == NULL) { + if (overlay == true) { + sourcePos.error("Resource %s appears in overlay but not" + " in the base package.\n", String8(entry).string()); + return NULL; + } c = new ConfigList(entry, sourcePos); mConfigs.add(entry, c); pos = (int)mOrderedConfigs.size(); @@ -3390,6 +3527,7 @@ sp ResourceTable::getEntry(const String16& package, const String16& type, const String16& name, const SourcePos& sourcePos, + bool overlay, const ResTable_config* config, bool doSetIndex) { @@ -3397,7 +3535,7 @@ sp ResourceTable::getEntry(const String16& package, if (t == NULL) { return NULL; } - return t->getEntry(name, sourcePos, config, doSetIndex); + return t->getEntry(name, sourcePos, config, doSetIndex, overlay); } sp ResourceTable::getEntry(uint32_t resID, diff --git a/ResourceTable.h b/ResourceTable.h index 74ba326..ec4331a 100644 --- a/ResourceTable.h +++ b/ResourceTable.h @@ -15,6 +15,7 @@ using namespace std; +class XMLNode; class ResourceTable; enum { @@ -34,6 +35,12 @@ status_t compileXmlFile(const sp& assets, ResourceTable* table, int options = XML_COMPILE_STANDARD_RESOURCE); +status_t compileXmlFile(const sp& assets, + const sp& xmlTree, + const sp& target, + ResourceTable* table, + int options = XML_COMPILE_STANDARD_RESOURCE); + status_t compileResourceFile(Bundle* bundle, const sp& assets, const sp& in, @@ -89,6 +96,7 @@ public: const String16& name, const String16& bagParent, const ResTable_config* params = NULL, + bool overlay = false, bool replace = false, bool isId = false); @@ -410,7 +418,8 @@ public: sp getEntry(const String16& entry, const SourcePos& pos, const ResTable_config* config = NULL, - bool doSetIndex = false); + bool doSetIndex = false, + bool overlay = false); const SourcePos& getFirstPublicSourcePos() const { return *mFirstPublicSourcePos; } @@ -494,6 +503,7 @@ private: const String16& type, const String16& name, const SourcePos& pos, + bool overlay, const ResTable_config* config = NULL, bool doSetIndex = false); sp getEntry(uint32_t resID, diff --git a/XMLNode.cpp b/XMLNode.cpp index d476567..2a85bc7 100644 --- a/XMLNode.cpp +++ b/XMLNode.cpp @@ -524,12 +524,30 @@ const Vector >& XMLNode::getChildren() const return mChildren; } +const String8& XMLNode::getFilename() const +{ + return mFilename; +} + const Vector& XMLNode::getAttributes() const { return mAttributes; } +const XMLNode::attribute_entry* XMLNode::getAttribute(const String16& ns, + const String16& name) const +{ + for (size_t i=0; i XMLNode::searchElement(const String16& tagNamespace, const String16& tagName) +{ + if (getType() == XMLNode::TYPE_ELEMENT + && mNamespaceUri == tagNamespace + && mElementName == tagName) { + return this; + } + + for (size_t i=0; i found = mChildren.itemAt(i)->searchElement(tagNamespace, tagName); + if (found != NULL) { + return found; + } + } + + return NULL; +} + +sp XMLNode::getChildElement(const String16& tagNamespace, const String16& tagName) +{ + for (size_t i=0; i child = mChildren.itemAt(i); + if (child->getType() == XMLNode::TYPE_ELEMENT + && child->mNamespaceUri == tagNamespace + && child->mElementName == tagName) { + return child; + } + } + + return NULL; +} + status_t XMLNode::addChild(const sp& child) { if (getType() == TYPE_CDATA) { @@ -561,6 +611,17 @@ status_t XMLNode::addChild(const sp& child) return NO_ERROR; } +status_t XMLNode::insertChildAt(const sp& child, size_t index) +{ + if (getType() == TYPE_CDATA) { + SourcePos(mFilename, child->getStartLineNumber()).error("Child to CDATA node."); + return UNKNOWN_ERROR; + } + //printf("Adding child %p to parent %p\n", child.get(), this); + mChildren.insertAt(child, index); + return NO_ERROR; +} + status_t XMLNode::addAttribute(const String16& ns, const String16& name, const String16& value) { diff --git a/XMLNode.h b/XMLNode.h index 86548a2..a9bea43 100644 --- a/XMLNode.h +++ b/XMLNode.h @@ -68,6 +68,8 @@ public: const String16& getElementName() const; const Vector >& getChildren() const; + const String8& getFilename() const; + struct attribute_entry { attribute_entry() : index(~(uint32_t)0), nameResId(0) { @@ -91,6 +93,8 @@ public: const Vector& getAttributes() const; + const attribute_entry* getAttribute(const String16& ns, const String16& name) const; + const String16& getCData() const; const String16& getComment() const; @@ -98,8 +102,14 @@ public: int32_t getStartLineNumber() const; int32_t getEndLineNumber() const; + sp searchElement(const String16& tagNamespace, const String16& tagName); + + sp getChildElement(const String16& tagNamespace, const String16& tagName); + status_t addChild(const sp& child); + status_t insertChildAt(const sp& child, size_t index); + status_t addAttribute(const String16& ns, const String16& name, const String16& value); -- 2.45.2