mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false),
mUpdate(false), mExtending(false),
mRequireLocalization(false), mPseudolocalize(false),
- mValues(false),
- mCompressionMethod(0), mOutputAPKFile(NULL),
+ mUTF8(false), mEncodingSpecified(false), mValues(false),
+ mCompressionMethod(0), mOutputAPKFile(NULL), mManifestPackageNameOverride(NULL),
mAssetSourceDir(NULL), mProguardFile(NULL),
mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
mRClassDir(NULL), mResourceIntermediatesDir(NULL),
void setRequireLocalization(bool val) { mRequireLocalization = val; }
bool getPseudolocalize(void) const { return mPseudolocalize; }
void setPseudolocalize(bool val) { mPseudolocalize = val; }
+ bool getUTF8(void) const { return mUTF8; }
+ void setUTF8(bool val) { mUTF8 = val; }
+ bool getEncodingSpecified(void) const { return mEncodingSpecified; }
+ void setEncodingSpecified(bool val) { mEncodingSpecified = val; }
bool getValues(void) const { return mValues; }
void setValues(bool val) { mValues = val; }
int getCompressionMethod(void) const { return mCompressionMethod; }
void setJunkPath(bool val) { mJunkPath = val; }
const char* getOutputAPKFile() const { return mOutputAPKFile; }
void setOutputAPKFile(const char* val) { mOutputAPKFile = val; }
+ const char* getManifestPackageNameOverride() const { return mManifestPackageNameOverride; }
+ void setManifestPackageNameOverride(const char * val) { mManifestPackageNameOverride = val; }
/*
* Input options.
void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); }
const char* getMinSdkVersion() const { return mMinSdkVersion; }
- void setMinSdkVersion(const char* val) { mMinSdkVersion = val; }
+ void setMinSdkVersion(const char* val) {
+ mMinSdkVersion = val;
+ if (!mEncodingSpecified) {
+ setUTF8(isUTF8Available());
+ }
+ }
const char* getTargetSdkVersion() const { return mTargetSdkVersion; }
void setTargetSdkVersion(const char* val) { mTargetSdkVersion = val; }
const char* getMaxSdkVersion() const { return mMaxSdkVersion; }
bool mExtending;
bool mRequireLocalization;
bool mPseudolocalize;
+ bool mUTF8;
+ bool mEncodingSpecified;
bool mValues;
int mCompressionMethod;
bool mJunkPath;
const char* mOutputAPKFile;
+ const char* mManifestPackageNameOverride;
const char* mAssetSourceDir;
const char* mProguardFile;
const char* mAndroidManifestFile;
/* misc stuff */
int mPackageCount;
#endif
+
+ /* UTF-8 is only available on APIs 7 or above or
+ * SDK levels that have code names.
+ */
+ bool isUTF8Available() {
+ char *end;
+ int minSdkNum = (int)strtol(mMinSdkVersion, &end, 0);
+ if (*end == '\0') {
+ if (minSdkNum < 7) {
+ return false;
+ }
+ }
+ return true;
+ }
};
#endif // __BUNDLE_H
}
tree.restart();
printXMLBlock(&tree);
+ tree.uninit();
delete asset;
asset = NULL;
}
" [-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] [--custom-package VAL] \\\n"
+ " [--app-version-name TEXT] [--custom-package VAL] [--utf16] \\\n"
" [-I base-package [-I base-package ...]] \\\n"
" [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n"
" [-S resource-sources [-S resource-sources ...]] "
" be stored compressed in the .apk. An empty string means to not\n"
" compress any files at all.\n"
" --min-sdk-version\n"
- " inserts android:minSdkVersion in to manifest.\n"
+ " inserts android:minSdkVersion in to manifest. If the version is 7 or\n"
+ " higher, the default encoding for resources will be in UTF-8.\n"
" --target-sdk-version\n"
" inserts android:targetSdkVersion in to manifest.\n"
" --max-sdk-version\n"
" --version-name\n"
" inserts android:versionName in to manifest.\n"
" --custom-package\n"
- " generates R.java into a different package.\n");
+ " generates R.java into a different package.\n"
+ " --utf16\n"
+ " changes default encoding for resources to UTF-16. Only useful when API\n"
+ " level is set to 7 or higher where the default encoding is UTF-8.\n");
}
/*
goto bail;
}
bundle.setCustomPackage(argv[0]);
+ } else if (strcmp(cp, "-utf16") == 0) {
+ bundle.setEncodingSpecified(true);
+ bundle.setUTF8(false);
+ } else if (strcmp(cp, "-rename-manifest-package") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--rename-manifest-package' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setManifestPackageNameOverride(argv[0]);
} else {
fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
wantUsage = true;
NULL, String8());
}
-static status_t parsePackage(const sp<AaptAssets>& assets, const sp<AaptGroup>& grp)
+static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
+ const sp<AaptGroup>& grp)
{
if (grp->getFiles().size() != 1) {
fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
+ String16 uses_sdk16("uses-sdk");
+ while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+ && code != ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::START_TAG) {
+ if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
+ ssize_t minSdkIndex = block.indexOfAttribute("android",
+ "minSdkVersion");
+ if (minSdkIndex >= 0) {
+ String8 minSdkString = String8(
+ block.getAttributeStringValue(minSdkIndex, &len));
+ bundle->setMinSdkVersion(minSdkString.string());
+ }
+ }
+ }
+ }
+
return NO_ERROR;
}
DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
baseGroup->getFiles();
for (size_t i=0; i < baseFiles.size(); i++) {
- printf("baseFile %d has flavor %s\n", i,
+ printf("baseFile %ld has flavor %s\n", i,
baseFiles.keyAt(i).toString().string());
}
for (size_t i=0; i < overlayFiles.size(); i++) {
- printf("overlayFile %d has flavor %s\n", i,
+ printf("overlayFile %ld has flavor %s\n", i,
overlayFiles.keyAt(i).toString().string());
}
}
keyAt(overlayGroupIndex));
if(baseFileIndex < UNKNOWN_ERROR) {
if (bundle->getVerbose()) {
- printf("found a match (%d) for overlay file %s, for flavor %s\n",
+ printf("found a match (%ld) for overlay file %s, for flavor %s\n",
baseFileIndex,
overlayGroup->getLeaf().string(),
overlayFiles.keyAt(overlayGroupIndex).toString().string());
node->addAttribute(ns, attr, String16(value));
}
+static void fullyQualifyClassName(String8& package, sp<XMLNode> node) {
+ XMLNode::attribute_entry* attr = node->editAttribute(
+ String16("http://schemas.android.com/apk/res/android"), String16("name"));
+ if (attr != NULL) {
+ String8 name(attr->string);
+
+ // asdf --> package.asdf
+ // .asdf .a.b --> package.asdf package.a.b
+ // asdf.adsf --> asdf.asdf
+ String8 className;
+ const char* p = name.string();
+ const char* q = strchr(p, '.');
+ if (p == q) {
+ className += package;
+ className += name;
+ } else if (q == NULL) {
+ className += package;
+ className += ".";
+ className += name;
+ } else {
+ className += name;
+ }
+ NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string()));
+ attr->string.setTo(String16(className));
+ }
+}
+
status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
{
root = root->searchElement(String16(), String16("manifest"));
addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
bundle->getMaxSdkVersion());
}
-
+
+ // Deal with manifest package name overrides
+ const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
+ if (manifestPackageNameOverride != NULL) {
+ // Update the actual package name
+ XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package"));
+ if (attr == NULL) {
+ fprintf(stderr, "package name is required with --rename-manifest-package.\n");
+ return UNKNOWN_ERROR;
+ }
+ String8 origPackage(attr->string);
+ attr->string.setTo(String16(manifestPackageNameOverride));
+ NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride));
+
+ // Make class names fully qualified
+ sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
+ if (application != NULL) {
+ fullyQualifyClassName(origPackage, application);
+
+ Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
+ for (size_t i = 0; i < children.size(); i++) {
+ sp<XMLNode> child = children.editItemAt(i);
+ String8 tag(child->getElementName());
+ if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
+ fullyQualifyClassName(origPackage, child);
+ }
+ }
+ }
+ }
+
return NO_ERROR;
}
return UNKNOWN_ERROR;
}
- status_t err = parsePackage(assets, androidManifestFile);
+ status_t err = parsePackage(bundle, assets, androidManifestFile);
if (err != NO_ERROR) {
return err;
}
NOISY(printf("Found %d included resource packages\n", (int)table.size()));
+ // Standard flags for compiled XML and optional UTF-8 encoding
+ int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
+ if (bundle->getUTF8()) {
+ xmlFlags |= XML_COMPILE_UTF8;
+ }
+
// --------------------------------------------------------------
// First, gather all resource information.
// --------------------------------------------------------------
ResourceDirIterator it(layouts, String8("layout"));
while ((err=it.next()) == NO_ERROR) {
String8 src = it.getFile()->getPrintableSource();
- err = compileXmlFile(assets, it.getFile(), &table);
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
if (err == NO_ERROR) {
ResXMLTree block;
block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
if (anims != NULL) {
ResourceDirIterator it(anims, String8("anim"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table);
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
if (xmls != NULL) {
ResourceDirIterator it(xmls, String8("xml"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table);
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
if (colors != NULL) {
ResourceDirIterator it(colors, String8("color"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table);
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
ResourceDirIterator it(menus, String8("menu"));
while ((err=it.next()) == NO_ERROR) {
String8 src = it.getFile()->getPrintableSource();
- err = compileXmlFile(assets, it.getFile(), &table);
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
table.writePublicDefinitions(String16(assets->getPackage()), fp);
fclose(fp);
}
-
+#if 0
NOISY(
ResTable rt;
rt.add(resFile->getData(), resFile->getSize(), NULL);
printf("Generated resources:\n");
rt.print();
)
-
+#endif
// These resources are now considered to be a part of the included
// resources, for others to reference.
err = assets->addIncludedResources(resFile);
root->removeWhitespace(false, NULL);
}
+ if ((options&XML_COMPILE_UTF8) != 0) {
+ root->setUTF8(true);
+ }
+
bool hasErrors = false;
if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
// Iterate through all data, collecting all values (strings,
// references, etc).
- StringPool valueStrings;
+ StringPool valueStrings = StringPool(false, bundle->getUTF8());
for (pi=0; pi<N; pi++) {
sp<Package> p = mOrderedPackages.itemAt(pi);
if (p->getTypes().size() == 0) {
continue;
}
- StringPool typeStrings;
- StringPool keyStrings;
+ StringPool typeStrings = StringPool(false, bundle->getUTF8());
+ StringPool keyStrings = StringPool(false, bundle->getUTF8());
const size_t N = p->getOrderedTypes().size();
for (size_t ti=0; ti<N; ti++) {
XML_COMPILE_COMPACT_WHITESPACE = 1<<2,
XML_COMPILE_STRIP_WHITESPACE = 1<<3,
XML_COMPILE_STRIP_RAW_VALUES = 1<<4,
+ XML_COMPILE_UTF8 = 1<<5,
XML_COMPILE_STANDARD_RESOURCE =
XML_COMPILE_STRIP_COMMENTS | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
const size_t NS = pool->size();
for (size_t s=0; s<NS; s++) {
size_t len;
- printf("String #%d: %s\n", s,
+ printf("String #%ld: %s\n", s,
String8(pool->stringAt(s, &len)).string());
}
}
-StringPool::StringPool(bool sorted)
- : mSorted(sorted), mValues(-1), mIdents(-1)
+StringPool::StringPool(bool sorted, bool utf8)
+ : mSorted(sorted), mUTF8(utf8), mValues(-1), mIdents(-1)
{
}
return err == NO_ERROR ? pool : NULL;
}
+#define ENCODE_LENGTH(str, chrsz, strSize) \
+{ \
+ size_t maxMask = 1 << ((chrsz*8)-1); \
+ size_t maxSize = maxMask-1; \
+ if (strSize > maxSize) { \
+ *str++ = maxMask | ((strSize>>(chrsz*8))&maxSize); \
+ } \
+ *str++ = strSize; \
+}
+
status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
{
// Allow appending. Sorry this is a little wacky.
return NO_MEMORY;
}
+ const size_t charSize = mUTF8 ? sizeof(uint8_t) : sizeof(char16_t);
+
size_t strPos = 0;
for (i=0; i<STRINGS; i++) {
entry& ent = mEntries.editItemAt(i);
const size_t strSize = (ent.value.size());
- const size_t lenSize = strSize > 0x7fff ? sizeof(uint32_t) : sizeof(uint16_t);
- const size_t totalSize = lenSize + ((strSize+1)*sizeof(uint16_t));
+ const size_t lenSize = strSize > (size_t)(1<<((charSize*8)-1))-1 ?
+ charSize*2 : charSize;
+
+ String8 encStr;
+ if (mUTF8) {
+ encStr = String8(ent.value);
+ }
+
+ const size_t encSize = mUTF8 ? encStr.size() : 0;
+ const size_t encLenSize = mUTF8 ?
+ (encSize > (size_t)(1<<((charSize*8)-1))-1 ?
+ charSize*2 : charSize) : 0;
ent.offset = strPos;
- uint16_t* dat = (uint16_t*)pool->editData(preSize + strPos + totalSize);
+
+ const size_t totalSize = lenSize + encLenSize +
+ ((mUTF8 ? encSize : strSize)+1)*charSize;
+
+ void* dat = (void*)pool->editData(preSize + strPos + totalSize);
if (dat == NULL) {
fprintf(stderr, "ERROR: Out of memory for string pool\n");
return NO_MEMORY;
}
- dat += (preSize+strPos)/sizeof(uint16_t);
- if (lenSize > sizeof(uint16_t)) {
- *dat = htods(0x8000 | ((strSize>>16)&0x7fff));
- dat++;
+ dat = (uint8_t*)dat + preSize + strPos;
+ if (mUTF8) {
+ uint8_t* strings = (uint8_t*)dat;
+
+ ENCODE_LENGTH(strings, sizeof(uint8_t), strSize)
+
+ ENCODE_LENGTH(strings, sizeof(uint8_t), encSize)
+
+ strncpy((char*)strings, encStr, encSize+1);
+ } else {
+ uint16_t* strings = (uint16_t*)dat;
+
+ ENCODE_LENGTH(strings, sizeof(uint16_t), strSize)
+
+ strcpy16_htod(strings, ent.value);
}
- *dat++ = htods(strSize);
- strcpy16_htod(dat, ent.value);
- strPos += lenSize + (strSize+1)*sizeof(uint16_t);
+ strPos += totalSize;
}
// Pad ending string position up to a uint32_t boundary.
if (mSorted) {
header->flags |= htodl(ResStringPool_header::SORTED_FLAG);
}
+ if (mUTF8) {
+ header->flags |= htodl(ResStringPool_header::UTF8_FLAG);
+ }
header->stringsStart = htodl(preSize);
header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0);
* lookup with ResStringPool::indexOfString() (O(log n)), at the expense
* of support for styled string entries (which requires the same string
* be included multiple times in the pool).
+ *
+ * If 'utf8' is true, strings will be encoded with UTF-8 instead of
+ * left in Java's native UTF-16.
*/
- explicit StringPool(bool sorted = false);
+ explicit StringPool(bool sorted = false, bool utf8 = false);
/**
* Add a new string to the pool. If mergeDuplicates is true, thenif
private:
const bool mSorted;
+ const bool mUTF8;
// Raw array of unique strings, in some arbitrary order.
Vector<entry> mEntries;
// Array of indices into mEntries, in the order they were
, mFilename(filename)
, mStartLineNumber(0)
, mEndLineNumber(0)
+ , mUTF8(false)
{
if (isNamespace) {
mNamespacePrefix = s1;
return NULL;
}
+XMLNode::attribute_entry* XMLNode::editAttribute(const String16& ns,
+ const String16& name)
+{
+ for (size_t i=0; i<mAttributes.size(); i++) {
+ attribute_entry * ae = &mAttributes.editItemAt(i);
+ if (ae->ns == ns && ae->name == name) {
+ return ae;
+ }
+ }
+
+ return NULL;
+}
+
const String16& XMLNode::getCData() const
{
return mChars;
status_t XMLNode::flatten(const sp<AaptFile>& dest,
bool stripComments, bool stripRawValues) const
{
- StringPool strings;
+ StringPool strings = StringPool(false, mUTF8);
Vector<uint32_t> resids;
// First collect just the strings for attribute names that have a
const attribute_entry* getAttribute(const String16& ns, const String16& name) const;
+ attribute_entry* editAttribute(const String16& ns, const String16& name);
+
const String16& getCData() const;
const String16& getComment() const;
void removeWhitespace(bool stripAll=true, const char** cDataTags=NULL);
+ void setUTF8(bool val) { mUTF8 = val; }
+
status_t parseValues(const sp<AaptAssets>& assets, ResourceTable* table);
status_t assignResourceIds(const sp<AaptAssets>& assets,
String8 mFilename;
int32_t mStartLineNumber;
int32_t mEndLineNumber;
+
+ // Encode compiled XML with UTF-8 StringPools?
+ bool mUTF8;
};
#endif