summaryrefslogtreecommitdiff
path: root/tools/aapt/Command.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt/Command.cpp')
-rw-r--r--tools/aapt/Command.cpp491
1 files changed, 377 insertions, 114 deletions
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 34292fe4c5b3..cc0da1568eb8 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,6 +3,7 @@
//
// Android Asset Packaging Tool main entry point.
//
+#include "ApkBuilder.h"
#include "Main.h"
#include "Bundle.h"
#include "ResourceFilter.h"
@@ -25,8 +26,9 @@ using namespace android;
*/
int doVersion(Bundle* bundle)
{
- if (bundle->getFileSpecCount() != 0)
+ if (bundle->getFileSpecCount() != 0) {
printf("(ignoring extra arguments)\n");
+ }
printf("Android Asset Packaging Tool, v0.2\n");
return 0;
@@ -46,13 +48,14 @@ ZipFile* openReadOnly(const char* fileName)
zip = new ZipFile;
result = zip->open(fileName, ZipFile::kOpenReadOnly);
if (result != NO_ERROR) {
- if (result == NAME_NOT_FOUND)
+ if (result == NAME_NOT_FOUND) {
fprintf(stderr, "ERROR: '%s' not found\n", fileName);
- else if (result == PERMISSION_DENIED)
+ } else if (result == PERMISSION_DENIED) {
fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
- else
+ } else {
fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
fileName);
+ }
delete zip;
return NULL;
}
@@ -73,8 +76,9 @@ ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
int flags;
flags = ZipFile::kOpenReadWrite;
- if (okayToCreate)
+ if (okayToCreate) {
flags |= ZipFile::kOpenCreate;
+ }
zip = new ZipFile;
result = zip->open(fileName, flags);
@@ -94,12 +98,13 @@ bail:
*/
const char* compressionName(int method)
{
- if (method == ZipEntry::kCompressStored)
+ if (method == ZipEntry::kCompressStored) {
return "Stored";
- else if (method == ZipEntry::kCompressDeflated)
+ } else if (method == ZipEntry::kCompressDeflated) {
return "Deflated";
- else
+ } else {
return "Unknown";
+ }
}
/*
@@ -107,10 +112,11 @@ const char* compressionName(int method)
*/
int calcPercent(long uncompressedLen, long compressedLen)
{
- if (!uncompressedLen)
+ if (!uncompressedLen) {
return 0;
- else
+ } else {
return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
+ }
}
/*
@@ -135,8 +141,9 @@ int doList(Bundle* bundle)
zipFileName = bundle->getFileSpecEntry(0);
zip = openReadOnly(zipFileName);
- if (zip == NULL)
+ if (zip == NULL) {
goto bail;
+ }
int count, i;
@@ -248,7 +255,9 @@ String8 getAttribute(const ResXMLTree& tree, const char* ns,
Res_value value;
if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) *outError = "attribute is not a string value";
+ if (outError != NULL) {
+ *outError = "attribute is not a string value";
+ }
return String8();
}
}
@@ -266,7 +275,9 @@ static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* o
Res_value value;
if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) *outError = "attribute is not a string value";
+ if (outError != NULL) {
+ *outError = "attribute is not a string value";
+ }
return String8();
}
}
@@ -286,7 +297,9 @@ static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
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";
+ if (outError != NULL) {
+ *outError = "attribute is not an integer value";
+ }
return defValue;
}
}
@@ -307,7 +320,9 @@ static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXM
}
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";
+ if (outError != NULL) {
+ *outError = "attribute is not an integer value";
+ }
return defValue;
}
}
@@ -330,7 +345,9 @@ static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree&
}
resTable->resolveReference(&value, 0);
if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) *outError = "attribute is not a string value";
+ if (outError != NULL) {
+ *outError = "attribute is not a string value";
+ }
return String8();
}
}
@@ -340,6 +357,49 @@ static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree&
return str ? String8(str, len) : String8();
}
+static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
+ const ResXMLTree& tree, uint32_t attrRes, String8* outError)
+{
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ if (outError != NULL) {
+ *outError = "attribute could not be found";
+ }
+ return;
+ }
+ if (tree.getAttributeValue(idx, value) != NO_ERROR) {
+ if (value->dataType == Res_value::TYPE_REFERENCE) {
+ resTable->resolveReference(value, 0);
+ }
+ // The attribute was found and was resolved if need be.
+ return;
+ }
+ if (outError != NULL) {
+ *outError = "error getting resolved resource attribute";
+ }
+}
+
+static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
+ uint32_t attrRes, String8 attrLabel, String8* outError)
+{
+ Res_value value;
+ getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
+ if (*outError != "") {
+ *outError = "error print resolved resource attribute";
+ return;
+ }
+ if (value.dataType == Res_value::TYPE_STRING) {
+ String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
+ printf("%s='%s'", attrLabel.string(),
+ ResTable::normalizeForOutput(result.string()).string());
+ } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
+ value.dataType <= Res_value::TYPE_LAST_INT) {
+ printf("%s='%d'", attrLabel.string(), value.data);
+ } else {
+ printf("%s='0x%x'", attrLabel.string(), (int)value.data);
+ }
+}
+
// These are attribute resource constants for the platform, as found
// in android.R.attr
enum {
@@ -349,6 +409,7 @@ enum {
PERMISSION_ATTR = 0x01010006,
RESOURCE_ATTR = 0x01010025,
DEBUGGABLE_ATTR = 0x0101000f,
+ VALUE_ATTR = 0x01010024,
VERSION_CODE_ATTR = 0x0101021b,
VERSION_NAME_ATTR = 0x0101021c,
SCREEN_ORIENTATION_ATTR = 0x0101001e,
@@ -378,7 +439,7 @@ enum {
BANNER_ATTR = 0x10103f2,
};
-const char *getComponentName(String8 &pkgName, String8 &componentName) {
+String8 getComponentName(String8 &pkgName, String8 &componentName) {
ssize_t idx = componentName.find(".");
String8 retStr(pkgName);
if (idx == 0) {
@@ -387,12 +448,12 @@ const char *getComponentName(String8 &pkgName, String8 &componentName) {
retStr += ".";
retStr += componentName;
} else {
- return componentName.string();
+ return componentName;
}
- return retStr.string();
+ return retStr;
}
-static void printCompatibleScreens(ResXMLTree& tree) {
+static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
size_t len;
ResXMLTree::event_code_t code;
int depth = 0;
@@ -410,7 +471,12 @@ static void printCompatibleScreens(ResXMLTree& tree) {
continue;
}
depth++;
- String8 tag(tree.getElementName(&len));
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL) {
+ *outError = "failed to get XML element name (bad string pool)";
+ return;
+ }
+ String8 tag(ctag16);
if (tag == "screen") {
int32_t screenSize = getIntegerAttribute(tree,
SCREEN_SIZE_ATTR, NULL, -1);
@@ -428,6 +494,29 @@ static void printCompatibleScreens(ResXMLTree& tree) {
printf("\n");
}
+static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
+ printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
+ if (maxSdkVersion != -1) {
+ printf(" maxSdkVersion='%d'", maxSdkVersion);
+ }
+ printf("\n");
+
+ if (optional) {
+ printf("optional-permission: name='%s'",
+ ResTable::normalizeForOutput(name.string()).string());
+ if (maxSdkVersion != -1) {
+ printf(" maxSdkVersion='%d'", maxSdkVersion);
+ }
+ printf("\n");
+ }
+}
+
+static void printUsesImpliedPermission(const String8& name, const String8& reason) {
+ printf("uses-implied-permission: name='%s' reason='%s'\n",
+ ResTable::normalizeForOutput(name.string()).string(),
+ ResTable::normalizeForOutput(reason.string()).string());
+}
+
Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
String8 *outError = NULL)
{
@@ -452,7 +541,12 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
depth--;
- String8 tag(tree.getElementName(&len));
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL) {
+ *outError = "failed to get XML element name (bad string pool)";
+ return Vector<String8>();
+ }
+ String8 tag(ctag16);
if (depth == 0 && tag == serviceTagName) {
withinApduService = false;
@@ -460,7 +554,12 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool
} else if (code == ResXMLTree::START_TAG) {
depth++;
- String8 tag(tree.getElementName(&len));
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL) {
+ *outError = "failed to get XML element name (bad string pool)";
+ return Vector<String8>();
+ }
+ String8 tag(ctag16);
if (depth == 1) {
if (tag == serviceTagName) {
@@ -535,6 +634,9 @@ int doDump(Bundle* bundle)
if (&res == NULL) {
fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
goto bail;
+ } else if (res.getError() != NO_ERROR) {
+ fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
+ goto bail;
}
if (strcmp("resources", option) == 0) {
@@ -627,7 +729,12 @@ int doDump(Bundle* bundle)
continue;
}
depth++;
- String8 tag(tree.getElementName(&len));
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL) {
+ fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
+ goto bail;
+ }
+ String8 tag(ctag16);
//printf("Depth %d tag %s\n", depth, tag.string());
if (depth == 1) {
if (tag != "manifest") {
@@ -635,7 +742,7 @@ int doDump(Bundle* bundle)
goto bail;
}
String8 pkg = getAttribute(tree, NULL, "package", NULL);
- printf("package: %s\n", pkg.string());
+ printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
} else if (depth == 2 && tag == "permission") {
String8 error;
String8 name = getAttribute(tree, NAME_ATTR, &error);
@@ -643,7 +750,8 @@ int doDump(Bundle* bundle)
fprintf(stderr, "ERROR: %s\n", error.string());
goto bail;
}
- printf("permission: %s\n", name.string());
+ printf("permission: %s\n",
+ ResTable::normalizeForOutput(name.string()).string());
} else if (depth == 2 && tag == "uses-permission") {
String8 error;
String8 name = getAttribute(tree, NAME_ATTR, &error);
@@ -651,11 +759,9 @@ int doDump(Bundle* bundle)
fprintf(stderr, "ERROR: %s\n", error.string());
goto bail;
}
- printf("uses-permission: %s\n", name.string());
- int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
- if (!req) {
- printf("optional-permission: %s\n", name.string());
- }
+ printUsesPermission(name,
+ getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
+ getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
}
}
} else if (strcmp("badging", option) == 0) {
@@ -668,7 +774,9 @@ int doDump(Bundle* bundle)
const size_t NC = configs.size();
for (size_t i=0; i<NC; i++) {
int dens = configs[i].density;
- if (dens == 0) dens = 160;
+ if (dens == 0) {
+ dens = 160;
+ }
densities.add(dens);
}
@@ -802,7 +910,8 @@ int doDump(Bundle* bundle)
printf("supports-input: '");
const size_t N = supportedInput.size();
for (size_t i=0; i<N; i++) {
- printf("%s", supportedInput[i].string());
+ printf("%s", ResTable::normalizeForOutput(
+ supportedInput[i].string()).string());
if (i != N - 1) {
printf("' '");
} else {
@@ -815,25 +924,27 @@ int doDump(Bundle* bundle)
withinSupportsInput = false;
} else if (depth < 3) {
if (withinActivity && isMainActivity) {
- const char *aName = getComponentName(pkg, activityName);
+ String8 aName(getComponentName(pkg, activityName));
if (isLauncherActivity) {
printf("launchable-activity:");
- if (aName != NULL) {
- printf(" name='%s' ", aName);
+ if (aName.length() > 0) {
+ printf(" name='%s' ",
+ ResTable::normalizeForOutput(aName.string()).string());
}
printf(" label='%s' icon='%s'\n",
- activityLabel.string(),
- activityIcon.string());
+ ResTable::normalizeForOutput(activityLabel.string()).string(),
+ ResTable::normalizeForOutput(activityIcon.string()).string());
}
if (isLeanbackLauncherActivity) {
printf("leanback-launchable-activity:");
- if (aName != NULL) {
- printf(" name='%s' ", aName);
+ if (aName.length() > 0) {
+ printf(" name='%s' ",
+ ResTable::normalizeForOutput(aName.string()).string());
}
printf(" label='%s' icon='%s' banner='%s'\n",
- activityLabel.string(),
- activityIcon.string(),
- activityBanner.string());
+ ResTable::normalizeForOutput(activityLabel.string()).string(),
+ ResTable::normalizeForOutput(activityIcon.string()).string(),
+ ResTable::normalizeForOutput(activityBanner.string()).string());
}
}
if (!hasIntentFilter) {
@@ -882,7 +993,13 @@ int doDump(Bundle* bundle)
continue;
}
depth++;
- String8 tag(tree.getElementName(&len));
+
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL) {
+ fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
+ goto bail;
+ }
+ String8 tag(ctag16);
//printf("Depth %d, %s\n", depth, tag.string());
if (depth == 1) {
if (tag != "manifest") {
@@ -890,7 +1007,8 @@ int doDump(Bundle* bundle)
goto bail;
}
pkg = getAttribute(tree, NULL, "package", NULL);
- printf("package: name='%s' ", pkg.string());
+ printf("package: name='%s' ",
+ ResTable::normalizeForOutput(pkg.string()).string());
int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
@@ -906,7 +1024,8 @@ int doDump(Bundle* bundle)
fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
goto bail;
}
- printf("versionName='%s'\n", versionName.string());
+ printf("versionName='%s'\n",
+ ResTable::normalizeForOutput(versionName.string()).string());
} else if (depth == 2) {
withinApplication = false;
if (tag == "application") {
@@ -921,13 +1040,14 @@ int doDump(Bundle* bundle)
if (llabel != "") {
if (localeStr == NULL || strlen(localeStr) == 0) {
label = llabel;
- printf("application-label:'%s'\n", llabel.string());
+ printf("application-label:'%s'\n",
+ ResTable::normalizeForOutput(llabel.string()).string());
} else {
if (label == "") {
label = llabel;
}
printf("application-label-%s:'%s'\n", localeStr,
- llabel.string());
+ ResTable::normalizeForOutput(llabel.string()).string());
}
}
}
@@ -939,7 +1059,8 @@ int doDump(Bundle* bundle)
assets.setConfiguration(tmpConfig);
String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
if (icon != "") {
- printf("application-icon-%d:'%s'\n", densities[i], icon.string());
+ printf("application-icon-%d:'%s'\n", densities[i],
+ ResTable::normalizeForOutput(icon.string()).string());
}
}
assets.setConfiguration(config);
@@ -954,8 +1075,9 @@ int doDump(Bundle* bundle)
fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
goto bail;
}
- printf("application: label='%s' ", label.string());
- printf("icon='%s'\n", icon.string());
+ printf("application: label='%s' ",
+ ResTable::normalizeForOutput(label.string()).string());
+ printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
if (testOnly != 0) {
printf("testOnly='%d'\n", testOnly);
}
@@ -979,7 +1101,8 @@ int doDump(Bundle* bundle)
goto bail;
}
if (name == "Donut") targetSdk = 4;
- printf("sdkVersion:'%s'\n", name.string());
+ printf("sdkVersion:'%s'\n",
+ ResTable::normalizeForOutput(name.string()).string());
} else if (code != -1) {
targetSdk = code;
printf("sdkVersion:'%d'\n", code);
@@ -998,7 +1121,8 @@ int doDump(Bundle* bundle)
goto bail;
}
if (name == "Donut" && targetSdk < 4) targetSdk = 4;
- printf("targetSdkVersion:'%s'\n", name.string());
+ printf("targetSdkVersion:'%s'\n",
+ ResTable::normalizeForOutput(name.string()).string());
} else if (code != -1) {
if (targetSdk < code) {
targetSdk = code;
@@ -1103,7 +1227,8 @@ int doDump(Bundle* bundle)
specScreenLandscapeFeature = true;
}
printf("uses-feature%s:'%s'\n",
- req ? "" : "-not-required", name.string());
+ req ? "" : "-not-required",
+ ResTable::normalizeForOutput(name.string()).string());
} else {
int vers = getIntegerAttribute(tree,
GL_ES_VERSION_ATTR, &error);
@@ -1161,12 +1286,11 @@ int doDump(Bundle* bundle)
} else if (name == "android.permission.WRITE_CALL_LOG") {
hasWriteCallLogPermission = true;
}
- printf("uses-permission:'%s'\n", name.string());
- int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
- if (!req) {
- printf("optional-permission:'%s'\n", name.string());
- }
- } else {
+
+ printUsesPermission(name,
+ getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
+ getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+ } else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
@@ -1174,7 +1298,8 @@ int doDump(Bundle* bundle)
} else if (tag == "uses-package") {
String8 name = getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- printf("uses-package:'%s'\n", name.string());
+ printf("uses-package:'%s'\n",
+ ResTable::normalizeForOutput(name.string()).string());
} else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
@@ -1183,7 +1308,8 @@ int doDump(Bundle* bundle)
} else if (tag == "original-package") {
String8 name = getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- printf("original-package:'%s'\n", name.string());
+ printf("original-package:'%s'\n",
+ ResTable::normalizeForOutput(name.string()).string());
} else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
@@ -1192,14 +1318,20 @@ int doDump(Bundle* bundle)
} else if (tag == "supports-gl-texture") {
String8 name = getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- printf("supports-gl-texture:'%s'\n", name.string());
+ printf("supports-gl-texture:'%s'\n",
+ ResTable::normalizeForOutput(name.string()).string());
} else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
}
} else if (tag == "compatible-screens") {
- printCompatibleScreens(tree);
+ printCompatibleScreens(tree, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting compatible screens: %s\n",
+ error.string());
+ goto bail;
+ }
depth--;
} else if (tag == "package-verifier") {
String8 name = getAttribute(tree, NAME_ATTR, &error);
@@ -1207,7 +1339,8 @@ int doDump(Bundle* bundle)
String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
if (publicKey != "" && error == "") {
printf("package-verifier: name='%s' publicKey='%s'\n",
- name.string(), publicKey.string());
+ ResTable::normalizeForOutput(name.string()).string(),
+ ResTable::normalizeForOutput(publicKey.string()).string());
}
}
}
@@ -1276,7 +1409,8 @@ int doDump(Bundle* bundle)
int req = getIntegerAttribute(tree,
REQUIRED_ATTR, NULL, 1);
printf("uses-library%s:'%s'\n",
- req ? "" : "-not-required", libraryName.string());
+ req ? "" : "-not-required", ResTable::normalizeForOutput(
+ libraryName.string()).string());
} else if (tag == "receiver") {
withinReceiver = true;
receiverName = getAttribute(tree, NAME_ATTR, &error);
@@ -1302,8 +1436,8 @@ int doDump(Bundle* bundle)
serviceName = getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:name' attribute for"
- " service: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:name' attribute for "
+ "service:%s\n", error.string());
goto bail;
}
@@ -1322,15 +1456,39 @@ int doDump(Bundle* bundle)
fprintf(stderr, "ERROR getting 'android:permission' attribute for"
" service '%s': %s\n", serviceName.string(), error.string());
}
- }
- } else if (withinSupportsInput && tag == "input-type") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
- if (name != "" && error == "") {
- supportedInput.add(name);
- } else {
- fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
- error.string());
- goto bail;
+ } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
+ String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:name' attribute for "
+ "meta-data:%s\n", error.string());
+ goto bail;
+ }
+ printf("meta-data: name='%s' ",
+ ResTable::normalizeForOutput(metaDataName.string()).string());
+ printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
+ &error);
+ if (error != "") {
+ // Try looking for a RESOURCE_ATTR
+ error = "";
+ printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
+ String8("resource"), &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:value' or "
+ "'android:resource' attribute for "
+ "meta-data:%s\n", error.string());
+ goto bail;
+ }
+ }
+ printf("\n");
+ } else if (withinSupportsInput && tag == "input-type") {
+ String8 name = getAttribute(tree, NAME_ATTR, &error);
+ if (name != "" && error == "") {
+ supportedInput.add(name);
+ } else {
+ fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
+ error.string());
+ goto bail;
+ }
}
}
} else if (depth == 4) {
@@ -1387,14 +1545,16 @@ int doDump(Bundle* bundle)
}
}
}
- } else if ((depth == 5) && withinIntentFilter){
+ } else if ((depth == 5) && withinIntentFilter) {
String8 action;
if (tag == "action") {
action = getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
+ error.string());
goto bail;
}
+
if (withinActivity) {
if (action == "android.intent.action.MAIN") {
isMainActivity = true;
@@ -1429,7 +1589,8 @@ int doDump(Bundle* bundle)
if (tag == "category") {
String8 category = getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
+ error.string());
goto bail;
}
if (withinActivity) {
@@ -1446,15 +1607,15 @@ int doDump(Bundle* bundle)
// Pre-1.6 implicitly granted permission compatibility logic
if (targetSdk < 4) {
if (!hasWriteExternalStoragePermission) {
- printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
- printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
- "'targetSdkVersion < 4'\n");
+ printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
+ printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
+ String8("targetSdkVersion < 4"));
hasWriteExternalStoragePermission = true;
}
if (!hasReadPhoneStatePermission) {
- printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
- printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
- "'targetSdkVersion < 4'\n");
+ printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
+ printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
+ String8("targetSdkVersion < 4"));
}
}
@@ -1463,22 +1624,22 @@ int doDump(Bundle* bundle)
// do this (regardless of target API version) because we can't have
// an app with write permission but not read permission.
if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
- printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
- printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
- "'requested WRITE_EXTERNAL_STORAGE'\n");
+ printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
+ printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
+ String8("requested WRITE_EXTERNAL_STORAGE"));
}
// Pre-JellyBean call log permission compatibility.
if (targetSdk < 16) {
if (!hasReadCallLogPermission && hasReadContactsPermission) {
- printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
- printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
- "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
+ printUsesPermission(String8("android.permission.READ_CALL_LOG"));
+ printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
+ String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
}
if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
- printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
- printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
- "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
+ printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
+ printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
+ String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
}
}
@@ -1503,7 +1664,7 @@ int doDump(Bundle* bundle)
printf("uses-implied-feature:'android.hardware.camera'," \
"'requested android.hardware.camera.autofocus feature'\n");
} else if (hasCameraPermission) {
- // if app wants to use camera but didn't request the feature, we infer
+ // if app wants to use camera but didn't request the feature, we infer
// that it meant to, and further that it wants autofocus
// (which was the 1.0 - 1.5 behavior)
printf("uses-feature:'android.hardware.camera'\n");
@@ -1656,7 +1817,9 @@ int doDump(Bundle* bundle)
if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
&& requiresSmallestWidthDp > 0) {
int compatWidth = compatibleWidthLimitDp;
- if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
+ if (compatWidth <= 0) {
+ compatWidth = requiresSmallestWidthDp;
+ }
if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
smallScreen = -1;
} else {
@@ -1701,10 +1864,18 @@ int doDump(Bundle* bundle)
|| compatibleWidthLimitDp > 0) ? -1 : 0;
}
printf("supports-screens:");
- if (smallScreen != 0) printf(" 'small'");
- if (normalScreen != 0) printf(" 'normal'");
- if (largeScreen != 0) printf(" 'large'");
- if (xlargeScreen != 0) printf(" 'xlarge'");
+ if (smallScreen != 0) {
+ printf(" 'small'");
+ }
+ if (normalScreen != 0) {
+ printf(" 'normal'");
+ }
+ if (largeScreen != 0) {
+ printf(" 'large'");
+ }
+ if (xlargeScreen != 0) {
+ printf(" 'xlarge'");
+ }
printf("\n");
printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
if (requiresSmallestWidthDp > 0) {
@@ -1740,7 +1911,8 @@ int doDump(Bundle* bundle)
if (dir->getFileCount() > 0) {
printf("native-code:");
for (size_t i=0; i<dir->getFileCount(); i++) {
- printf(" '%s'", dir->getFileName(i).string());
+ printf(" '%s'", ResTable::normalizeForOutput(
+ dir->getFileName(i).string()).string());
}
printf("\n");
}
@@ -1813,7 +1985,8 @@ int doAdd(Bundle* bundle)
} else {
if (bundle->getJunkPath()) {
String8 storageName = String8(fileName).getPathLeaf();
- printf(" '%s' as '%s'...\n", fileName, storageName.string());
+ printf(" '%s' as '%s'...\n", fileName,
+ ResTable::normalizeForOutput(storageName.string()).string());
result = zip->add(fileName, storageName.string(),
bundle->getCompressionMethod(), NULL);
} else {
@@ -1823,12 +1996,13 @@ int doAdd(Bundle* bundle)
}
if (result != NO_ERROR) {
fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
- if (result == NAME_NOT_FOUND)
+ if (result == NAME_NOT_FOUND) {
fprintf(stderr, ": file not found\n");
- else if (result == ALREADY_EXISTS)
+ } else if (result == ALREADY_EXISTS) {
fprintf(stderr, ": already exists in archive\n");
- else
+ } else {
fprintf(stderr, "\n");
+ }
goto bail;
}
}
@@ -1895,6 +2069,58 @@ bail:
return (result != NO_ERROR);
}
+static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
+ const size_t numDirs = dir->getDirs().size();
+ for (size_t i = 0; i < numDirs; i++) {
+ bool ignore = ignoreConfig;
+ const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
+ const char* dirStr = subDir->getLeaf().string();
+ if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
+ ignore = true;
+ }
+ status_t err = addResourcesToBuilder(subDir, builder, ignore);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ const size_t numFiles = dir->getFiles().size();
+ for (size_t i = 0; i < numFiles; i++) {
+ sp<AaptGroup> gp = dir->getFiles().valueAt(i);
+ const size_t numConfigs = gp->getFiles().size();
+ for (size_t j = 0; j < numConfigs; j++) {
+ status_t err = NO_ERROR;
+ if (ignoreConfig) {
+ err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
+ } else {
+ err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
+ }
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to add %s (%s) to builder.\n",
+ gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
+ return err;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
+ if (split->isBase()) {
+ return original;
+ }
+
+ String8 ext(original.getPathExtension());
+ if (ext == String8(".apk")) {
+ return String8::format("%s_%s%s",
+ original.getBasePath().string(),
+ split->getDirectorySafeName().string(),
+ ext.string());
+ }
+
+ return String8::format("%s_%s", original.string(),
+ split->getDirectorySafeName().string());
+}
/*
* Package up an asset directory and associated application files.
@@ -1908,17 +2134,18 @@ int doPackage(Bundle* bundle)
int N;
FILE* fp;
String8 dependencyFile;
+ sp<ApkBuilder> builder;
// -c en_XA or/and ar_XB means do pseudolocalization
- ResourceFilter filter;
- err = filter.parse(bundle->getConfigurations());
+ sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
+ err = configFilter->parse(bundle->getConfigurations());
if (err != NO_ERROR) {
goto bail;
}
- if (filter.containsPseudo()) {
+ if (configFilter->containsPseudo()) {
bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
}
- if (filter.containsPseudoBidi()) {
+ if (configFilter->containsPseudoBidi()) {
bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
}
@@ -1966,9 +2193,32 @@ int doPackage(Bundle* bundle)
assets->print(String8());
}
+ // Create the ApkBuilder, which will collect the compiled files
+ // to write to the final APK (or sets of APKs if we are building
+ // a Split APK.
+ builder = new ApkBuilder(configFilter);
+
+ // If we are generating a Split APK, find out which configurations to split on.
+ if (bundle->getSplitConfigurations().size() > 0) {
+ const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
+ const size_t numSplits = splitStrs.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ std::set<ConfigDescription> configs;
+ if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
+ fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
+ goto bail;
+ }
+
+ err = builder->createSplitForConfigs(configs);
+ if (err != NO_ERROR) {
+ goto bail;
+ }
+ }
+ }
+
// If they asked for any fileAs that need to be compiled, do so.
if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
- err = buildResources(bundle, assets);
+ err = buildResources(bundle, assets, builder);
if (err != 0) {
goto bail;
}
@@ -2055,11 +2305,24 @@ int doPackage(Bundle* bundle)
// Write the apk
if (outputAPKFile) {
- err = writeAPK(bundle, assets, String8(outputAPKFile));
+ // Gather all resources and add them to the APK Builder. The builder will then
+ // figure out which Split they belong in.
+ err = addResourcesToBuilder(assets, builder);
if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
goto bail;
}
+
+ const Vector<sp<ApkSplit> >& splits = builder->getSplits();
+ const size_t numSplits = splits.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ const sp<ApkSplit>& split = splits[i];
+ String8 outputPath = buildApkName(String8(outputAPKFile), split);
+ err = writeAPK(bundle, outputPath, split);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
+ goto bail;
+ }
+ }
}
// If we've been asked to generate a dependency file, we need to finish up here.
@@ -2096,7 +2359,7 @@ bail:
*
* POSTCONDITIONS
* Destination directory will be updated to match the PNG files in
- * the source directory.
+ * the source directory.
*/
int doCrunch(Bundle* bundle)
{