From cf244ada58539ce857ec041d7288d0271204fbb6 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 9 Mar 2010 15:00:30 -0800 Subject: [PATCH] Add ability for some manifest attributes to reference resources. This loosens our restriction on many manifest attributes requiring literal string values, to allow various ones to use values from resources. This is only allowed if the resource value does not change from configuration changes, and the restriction is still in place for attributes that are core to security (requesting permissions) or market operation (used libraries and features etc). Change-Id: I4da02f6a5196cb6a7dbcff9ac25403904c42c2c8 --- .../android/content/pm/PackageParser.java | 168 ++++++----- core/java/android/content/res/TypedArray.java | 36 +++ include/utils/ResourceTypes.h | 4 +- libs/utils/ResourceTypes.cpp | 9 +- tools/aapt/Command.cpp | 2 +- tools/aapt/Resource.cpp | 267 +++++++++++------- tools/aapt/ResourceTable.cpp | 14 + tools/aapt/ResourceTable.h | 6 + 8 files changed, 321 insertions(+), 185 deletions(-) diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5da7fd1197837..663d9e6050846 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -755,14 +755,14 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifest); pkg.mVersionCode = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_versionCode, 0); - pkg.mVersionName = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifest_versionName); + pkg.mVersionName = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifest_versionName, 0); if (pkg.mVersionName != null) { pkg.mVersionName = pkg.mVersionName.intern(); } - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifest_sharedUserId); - if (str != null) { + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0); + if (str != null && str.length() > 0) { String nameError = validateName(str, true); if (nameError != null && !"android".equals(pkgName)) { outError[0] = " specifies bad sharedUserId name \"" @@ -828,6 +828,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesPermission); + // Note: don't allow this value to be a reference to a resource + // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesPermission_name); @@ -871,6 +873,8 @@ public class PackageParser { FeatureInfo fi = new FeatureInfo(); sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesFeature); + // Note: don't allow this value to be a reference to a resource + // that may change. fi.name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesFeature_name); if (fi.name == null) { @@ -1002,6 +1006,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestProtectedBroadcast); + // Note: don't allow this value to be a reference to a resource + // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name); @@ -1027,8 +1033,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestOriginalPackage); - String orig =sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestOriginalPackage_name); + String orig =sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0); if (!pkg.packageName.equals(orig)) { if (pkg.mOriginalPackages == null) { pkg.mOriginalPackages = new ArrayList(); @@ -1045,8 +1051,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestOriginalPackage); - String name = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestOriginalPackage_name); + String name = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0); sa.recycle(); @@ -1271,6 +1277,8 @@ public class PackageParser { return null; } + // Note: don't allow this value to be a reference to a resource + // that may change. perm.info.group = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup); if (perm.info.group != null) { @@ -1375,6 +1383,8 @@ public class PackageParser { } String str; + // Note: don't allow this value to be a reference to a resource + // that may change. str = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage); a.info.targetPackage = str != null ? str.intern() : null; @@ -1415,8 +1425,8 @@ public class PackageParser { TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestApplication); - String name = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_name); + String name = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_name, 0); if (name != null) { ai.className = buildClassName(pkgName, name, outError); if (ai.className == null) { @@ -1426,8 +1436,8 @@ public class PackageParser { } } - String manageSpaceActivity = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity); + String manageSpaceActivity = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity, 0); if (manageSpaceActivity != null) { ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity, outError); @@ -1440,8 +1450,8 @@ public class PackageParser { // backupAgent, killAfterRestore, restoreNeedsApplication, and restoreAnyVersion // are only relevant if backup is possible for the given application. - String backupAgent = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_backupAgent); + String backupAgent = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_backupAgent, 0); if (backupAgent != null) { ai.backupAgentName = buildClassName(pkgName, backupAgent, outError); if (false) { @@ -1539,21 +1549,22 @@ public class PackageParser { } String str; - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_permission); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_permission, 0); ai.permission = (str != null && str.length() > 0) ? str.intern() : null; - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity, 0); ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName, str, outError); if (outError[0] == null) { - ai.processName = buildProcessName(ai.packageName, null, sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_process), + ai.processName = buildProcessName(ai.packageName, null, sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_process, 0), flags, mSeparateProcesses, outError); - ai.enabled = sa.getBoolean(com.android.internal.R.styleable.AndroidManifestApplication_enabled, true); + ai.enabled = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_enabled, true); } sa.recycle(); @@ -1632,6 +1643,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesLibrary); + // Note: don't allow this value to be a reference to a resource + // that may change. String lname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesLibrary_name); boolean req = sa.getBoolean( @@ -1681,7 +1694,7 @@ public class PackageParser { private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, String[] outError, String tag, TypedArray sa, int nameRes, int labelRes, int iconRes) { - String name = sa.getNonResourceString(nameRes); + String name = sa.getNonConfigurationString(nameRes, 0); if (name == null) { outError[0] = tag + " does not specify android:name"; return false; @@ -1747,16 +1760,16 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_theme, 0); String str; - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestActivity_permission); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestActivity_permission, 0); if (str == null) { a.info.permission = owner.applicationInfo.permission; } else { a.info.permission = str.length() > 0 ? str.toString().intern() : null; } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity, 0); a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName, owner.applicationInfo.taskAffinity, str, outError); @@ -1902,8 +1915,8 @@ public class PackageParser { TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestActivityAlias); - String targetActivity = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity); + String targetActivity = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity, 0); if (targetActivity == null) { outError[0] = " does not specify android:targetActivity"; sa.recycle(); @@ -1980,8 +1993,8 @@ public class PackageParser { } String str; - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestActivityAlias_permission); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestActivityAlias_permission, 0); if (str != null) { a.info.permission = str.length() > 0 ? str.toString().intern() : null; } @@ -2068,17 +2081,17 @@ public class PackageParser { p.info.exported = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_exported, true); - String cpname = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestProvider_authorities); + String cpname = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestProvider_authorities, 0); p.info.isSyncable = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_syncable, false); - String permission = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestProvider_permission); - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestProvider_readPermission); + String permission = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestProvider_permission, 0); + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestProvider_readPermission, 0); if (str == null) { str = permission; } @@ -2088,8 +2101,8 @@ public class PackageParser { p.info.readPermission = str.length() > 0 ? str.toString().intern() : null; } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestProvider_writePermission); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestProvider_writePermission, 0); if (str == null) { str = permission; } @@ -2152,20 +2165,20 @@ public class PackageParser { PatternMatcher pa = null; - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path); + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB); } @@ -2203,15 +2216,15 @@ public class PackageParser { PathPermission pa = null; - String permission = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_permission); - String readPermission = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission); + String permission = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_permission, 0); + String readPermission = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission, 0); if (readPermission == null) { readPermission = permission; } - String writePermission = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission); + String writePermission = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission, 0); if (writePermission == null) { writePermission = permission; } @@ -2238,22 +2251,22 @@ public class PackageParser { return false; } - String path = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_path); + String path = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_path, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL, readPermission, writePermission); } - path = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix); + path = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission, writePermission); } - path = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern); + path = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission); @@ -2335,8 +2348,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestService_exported, false); } - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestService_permission); + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestService_permission, 0); if (str == null) { s.info.permission = owner.applicationInfo.permission; } else { @@ -2433,8 +2446,8 @@ public class PackageParser { data = new Bundle(); } - String name = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestMetaData_name); + String name = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestMetaData_name, 0); if (name == null) { outError[0] = " requires an android:name attribute"; sa.recycle(); @@ -2552,8 +2565,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestData); - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_mimeType); + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_mimeType, 0); if (str != null) { try { outInfo.addDataType(str); @@ -2564,34 +2577,34 @@ public class PackageParser { } } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_scheme); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_scheme, 0); if (str != null) { outInfo.addDataScheme(str); } - String host = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_host); - String port = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_port); + String host = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_host, 0); + String port = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_port, 0); if (host != null) { outInfo.addDataAuthority(host, port); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_path); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_path, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_pathPrefix); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_pathPrefix, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_pathPattern); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_pathPattern, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB); } @@ -2754,7 +2767,7 @@ public class PackageParser { public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) { owner = args.owner; intents = new ArrayList(0); - String name = args.sa.getNonResourceString(args.nameRes); + String name = args.sa.getNonConfigurationString(args.nameRes, 0); if (name == null) { className = null; args.outError[0] = args.tag + " does not specify android:name"; @@ -2793,7 +2806,8 @@ public class PackageParser { if (args.processRes != 0) { outInfo.processName = buildProcessName(owner.applicationInfo.packageName, - owner.applicationInfo.processName, args.sa.getNonResourceString(args.processRes), + owner.applicationInfo.processName, + args.sa.getNonConfigurationString(args.processRes, 0), args.flags, args.sepProcesses, args.outError); } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index a7fb31de05f3e..09fdf8d637d40 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -147,6 +147,42 @@ public class TypedArray { return null; } + /** + * @hide + * Retrieve the string value for the attribute at index that is + * not allowed to change with the given configurations. + * + * @param index Index of attribute to retrieve. + * @param allowedChangingConfigs Bit mask of configurations from + * ActivityInfo that are allowed to change. + * + * @return String holding string data. Any styling information is + * removed. Returns null if the attribute is not defined. + */ + public String getNonConfigurationString(int index, int allowedChangingConfigs) { + index *= AssetManager.STYLE_NUM_ENTRIES; + final int[] data = mData; + final int type = data[index+AssetManager.STYLE_TYPE]; + if ((data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]&~allowedChangingConfigs) != 0) { + return null; + } + if (type == TypedValue.TYPE_NULL) { + return null; + } else if (type == TypedValue.TYPE_STRING) { + return loadStringValueAt(index).toString(); + } + + TypedValue v = mValue; + if (getValueAt(index, v)) { + Log.w(Resources.TAG, "Converting to string: " + v); + CharSequence cs = v.coerceToString(); + return cs != null ? cs.toString() : null; + } + Log.w(Resources.TAG, "getString of bad type: 0x" + + Integer.toHexString(type)); + return null; + } + /** * Retrieve the boolean value for the attribute at index. * diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 0e796dc561f1e..b701ce74d627d 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -631,6 +631,8 @@ public: void restart(); + const ResStringPool& getStrings() const; + event_code_t getEventType() const; // Note, unlike XmlPullParser, the first call to next() will return // START_TAG of the first element. @@ -716,8 +718,6 @@ public: void uninit(); - const ResStringPool& getStrings() const; - private: friend class ResXMLParser; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 38d8412952fc9..7e0f881af674a 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -625,6 +625,10 @@ void ResXMLParser::restart() mCurNode = NULL; mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; } +const ResStringPool& ResXMLParser::getStrings() const +{ + return mTree.mStrings; +} ResXMLParser::event_code_t ResXMLParser::getEventType() const { @@ -1149,11 +1153,6 @@ void ResXMLTree::uninit() restart(); } -const ResStringPool& ResXMLTree::getStrings() const -{ - return mStrings; -} - status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const { const uint16_t eventCode = dtohs(node->header.type); diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 537ae5e7716b0..735a80da9f2e1 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -603,7 +603,7 @@ int doDump(Bundle* bundle) } else { printf("versionCode='' "); } - String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error); + String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); goto bail; diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index b7580b33c7231..c0ebb5963328e 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -379,14 +379,64 @@ enum { ATTR_LEADING_SPACES = -3, ATTR_TRAILING_SPACES = -4 }; -static int validateAttr(const String8& path, const ResXMLParser& parser, +static int validateAttr(const String8& path, const ResTable& table, + const ResXMLParser& parser, const char* ns, const char* attr, const char* validChars, bool required) { size_t len; ssize_t index = parser.indexOfAttribute(ns, attr); const uint16_t* str; - if (index >= 0 && (str=parser.getAttributeStringValue(index, &len)) != NULL) { + Res_value value; + if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) { + const ResStringPool* pool = &parser.getStrings(); + if (value.dataType == Res_value::TYPE_REFERENCE) { + uint32_t specFlags = 0; + int strIdx; + if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr, + value.data); + return ATTR_NOT_FOUND; + } + + pool = table.getTableStringBlock(strIdx); + #if 0 + if (pool != NULL) { + str = pool->stringAt(value.data, &len); + } + printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr, + specFlags, strIdx, str != NULL ? String8(str).string() : "???"); + #endif + if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr, + specFlags); + return ATTR_NOT_FOUND; + } + } + if (value.dataType == Res_value::TYPE_STRING) { + if (pool == NULL) { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr); + return ATTR_NOT_FOUND; + } + if ((str=pool->stringAt(value.data, &len)) == NULL) { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr); + return ATTR_NOT_FOUND; + } + } else { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr, + value.dataType); + return ATTR_NOT_FOUND; + } if (validChars) { for (size_t i=0; i& assets) err = NO_ERROR; } + if (table.validateLocalizations()) { + hasErrors = true; + } + + if (hasErrors) { + return UNKNOWN_ERROR; + } + const sp manifestFile(androidManifestFile->getFiles().valueAt(0)); String8 manifestPath(manifestFile->getPrintableSource()); + // Generate final compiled manifest file. + manifestFile->clearData(); + 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; + } + + //block.restart(); + //printXMLBlock(&block); + + // -------------------------------------------------------------- + // Generate the final resource table. + // Re-flatten because we may have added new resource IDs + // -------------------------------------------------------------- + + ResTable finalResTable; + sp resFile; + + if (table.hasResources()) { + sp symbols = assets->getSymbolsFor(String8("R")); + err = table.addSymbols(symbols); + if (err < NO_ERROR) { + return err; + } + + resFile = getResourceFile(assets); + if (resFile == NULL) { + fprintf(stderr, "Error: unable to generate entry for resource data\n"); + return UNKNOWN_ERROR; + } + + err = table.flatten(bundle, resFile); + if (err < NO_ERROR) { + return err; + } + + if (bundle->getPublicOutputFile()) { + FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); + if (fp == NULL) { + fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", + (const char*)bundle->getPublicOutputFile(), strerror(errno)); + return UNKNOWN_ERROR; + } + if (bundle->getVerbose()) { + printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); + } + table.writePublicDefinitions(String16(assets->getPackage()), fp); + fclose(fp); + } + + // Read resources back in, + finalResTable.add(resFile->getData(), resFile->getSize(), NULL); + +#if 0 + NOISY( + printf("Generated resources:\n"); + finalResTable.print(); + ) +#endif + } + // Perform a basic validation of the manifest file. This time we // parse it with the comments intact, so that we can use them to // generate java docs... so we are not going to write this one // back out to the final manifest data. - err = compileXmlFile(assets, manifestFile, &table, + sp outManifestFile = new AaptFile(manifestFile->getSourceFile(), + manifestFile->getGroupEntry(), + manifestFile->getResourceType()); + err = compileXmlFile(assets, manifestFile, + outManifestFile, &table, XML_COMPILE_ASSIGN_ATTRIBUTE_IDS | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); if (err < NO_ERROR) { return err; } ResXMLTree block; - block.setTo(manifestFile->getData(), manifestFile->getSize(), true); + block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); String16 manifest16("manifest"); String16 permission16("permission"); String16 permission_group16("permission-group"); @@ -1012,16 +1143,20 @@ status_t buildResources(Bundle* bundle, const sp& assets) continue; } if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { - if (validateAttr(manifestPath, block, NULL, "package", + if (validateAttr(manifestPath, finalResTable, block, NULL, "package", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { + hasErrors = true; + } } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { const bool isGroup = strcmp16(block.getElementName(&len), permission_group16.string()) == 0; - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - isGroup ? packageIdentCharsWithTheStupid + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", isGroup ? packageIdentCharsWithTheStupid : packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } @@ -1099,56 +1234,56 @@ status_t buildResources(Bundle* bundle, const sp& assets) } syms->makeSymbolPublic(String8(e), srcPos); } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - packageIdentChars, true) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - classIdentChars, true) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "targetPackage", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - classIdentChars, false) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", classIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "taskAffinity", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - classIdentChars, true) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "authorities", authoritiesIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; @@ -1156,39 +1291,39 @@ status_t buildResources(Bundle* bundle, const sp& assets) } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - classIdentChars, true) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "taskAffinity", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 || strcmp16(block.getElementName(&len), category16.string()) == 0) { - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "mimeType", typeIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "scheme", schemeIdentChars, true) != ATTR_OKAY) { hasErrors = true; @@ -1197,76 +1332,7 @@ status_t buildResources(Bundle* bundle, const sp& assets) } } - if (table.validateLocalizations()) { - hasErrors = true; - } - - if (hasErrors) { - return UNKNOWN_ERROR; - } - - // Generate final compiled manifest file. - manifestFile->clearData(); - 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; - } - - //block.restart(); - //printXMLBlock(&block); - - // -------------------------------------------------------------- - // Generate the final resource table. - // Re-flatten because we may have added new resource IDs - // -------------------------------------------------------------- - - if (table.hasResources()) { - sp symbols = assets->getSymbolsFor(String8("R")); - err = table.addSymbols(symbols); - if (err < NO_ERROR) { - return err; - } - - sp resFile(getResourceFile(assets)); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } - - err = table.flatten(bundle, resFile); - if (err < NO_ERROR) { - return err; - } - - if (bundle->getPublicOutputFile()) { - FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); - if (fp == NULL) { - fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", - (const char*)bundle->getPublicOutputFile(), strerror(errno)); - return UNKNOWN_ERROR; - } - if (bundle->getVerbose()) { - printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); - } - 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 + if (resFile != NULL) { // These resources are now considered to be a part of the included // resources, for others to reference. err = assets->addIncludedResources(resFile); @@ -1275,6 +1341,7 @@ status_t buildResources(Bundle* bundle, const sp& assets) return err; } } + return err; } diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 1f9d15285785f..ab5e9376f3283 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -27,6 +27,20 @@ status_t compileXmlFile(const sp& assets, return compileXmlFile(assets, root, target, table, options); } +status_t compileXmlFile(const sp& assets, + const sp& target, + const sp& outTarget, + ResourceTable* table, + int options) +{ + sp root = XMLNode::parse(target); + if (root == NULL) { + return UNKNOWN_ERROR; + } + + return compileXmlFile(assets, root, outTarget, table, options); +} + status_t compileXmlFile(const sp& assets, const sp& root, const sp& target, diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 60d09012beda4..186c7ca8fdd8d 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -36,6 +36,12 @@ status_t compileXmlFile(const sp& assets, ResourceTable* table, int options = XML_COMPILE_STANDARD_RESOURCE); +status_t compileXmlFile(const sp& assets, + const sp& target, + const sp& outTarget, + ResourceTable* table, + int options = XML_COMPILE_STANDARD_RESOURCE); + status_t compileXmlFile(const sp& assets, const sp& xmlTree, const sp& target,