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.cpp1478
1 files changed, 940 insertions, 538 deletions
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 7e7d3465bbbc..258c7c7f964d 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,31 +3,41 @@
//
// Android Asset Packaging Tool main entry point.
//
-#include "Main.h"
+#include "AaptXml.h"
+#include "ApkBuilder.h"
#include "Bundle.h"
+#include "Images.h"
+#include "Main.h"
#include "ResourceFilter.h"
#include "ResourceTable.h"
-#include "Images.h"
#include "XMLNode.h"
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
#include <utils/Log.h>
+#include <utils/SortedVector.h>
#include <utils/threads.h>
-#include <utils/List.h>
-#include <utils/Errors.h>
+#include <utils/Vector.h>
-#include <fcntl.h>
#include <errno.h>
+#include <fcntl.h>
using namespace android;
+#ifndef AAPT_VERSION
+ #define AAPT_VERSION ""
+#endif
+
/*
* Show version info. All the cool kids do it.
*/
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");
+ }
+ printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
return 0;
}
@@ -46,13 +56,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 +84,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 +106,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 +120,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 +149,9 @@ int doList(Bundle* bundle)
zipFileName = bundle->getFileSpecEntry(0);
zip = openReadOnly(zipFileName);
- if (zip == NULL)
+ if (zip == NULL) {
goto bail;
+ }
int count, i;
@@ -228,117 +243,25 @@ bail:
return result;
}
-static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
-{
- size_t N = tree.getAttributeCount();
- for (size_t i=0; i<N; i++) {
- if (tree.getAttributeNameResID(i) == attrRes) {
- return (ssize_t)i;
- }
- }
- return -1;
-}
-
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
- const char* attr, String8* outError)
-{
- ssize_t idx = tree.indexOfAttribute(ns, attr);
- if (idx < 0) {
- return String8();
- }
- 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";
- return String8();
- }
- }
- size_t len;
- const char16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
-}
-
-static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return String8();
- }
- 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";
- return String8();
- }
- }
- size_t len;
- const char16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
-}
-
-static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
- String8* outError, int32_t defValue = -1)
+static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
+ uint32_t attrRes, String8 attrLabel, String8* outError)
{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return defValue;
- }
Res_value value;
- 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";
- return defValue;
- }
- }
- return value.data;
-}
-
-static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
- uint32_t attrRes, String8* outError, int32_t defValue = -1)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return defValue;
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- resTable->resolveReference(&value, 0);
- }
- 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 defValue;
- }
+ AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
+ if (*outError != "") {
+ *outError = "error print resolved resource attribute";
+ return;
}
- return value.data;
-}
-
-static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
- uint32_t attrRes, String8* outError)
-{
- ssize_t idx = indexOfAttribute(tree, attrRes);
- if (idx < 0) {
- return String8();
- }
- Res_value value;
- if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
- if (value.dataType == Res_value::TYPE_STRING) {
- size_t len;
- const char16_t* str = tree.getAttributeStringValue(idx, &len);
- return str ? String8(str, len) : String8();
- }
- resTable->resolveReference(&value, 0);
- if (value.dataType != Res_value::TYPE_STRING) {
- if (outError != NULL) *outError = "attribute is not a string value";
- return String8();
- }
+ if (value.dataType == Res_value::TYPE_STRING) {
+ String8 result = AaptXml::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);
}
- size_t len;
- const Res_value* value2 = &value;
- const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
- return str ? String8(str, len) : String8();
}
// These are attribute resource constants for the platform, as found
@@ -348,8 +271,11 @@ enum {
ICON_ATTR = 0x01010002,
NAME_ATTR = 0x01010003,
PERMISSION_ATTR = 0x01010006,
+ EXPORTED_ATTR = 0x01010010,
+ GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
RESOURCE_ATTR = 0x01010025,
DEBUGGABLE_ATTR = 0x0101000f,
+ VALUE_ATTR = 0x01010024,
VERSION_CODE_ATTR = 0x0101021b,
VERSION_NAME_ATTR = 0x0101021c,
SCREEN_ORIENTATION_ATTR = 0x0101001e,
@@ -369,6 +295,7 @@ enum {
LARGE_SCREEN_ATTR = 0x01010286,
XLARGE_SCREEN_ATTR = 0x010102bf,
REQUIRED_ATTR = 0x0101028e,
+ INSTALL_LOCATION_ATTR = 0x010102b7,
SCREEN_SIZE_ATTR = 0x010102ca,
SCREEN_DENSITY_ATTR = 0x010102cb,
REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
@@ -376,9 +303,10 @@ enum {
LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
PUBLIC_KEY_ATTR = 0x010103a6,
CATEGORY_ATTR = 0x010103e8,
+ 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 +315,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,12 +338,17 @@ 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);
- int32_t screenDensity = getIntegerAttribute(tree,
- SCREEN_DENSITY_ATTR, NULL, -1);
+ int32_t screenSize = AaptXml::getIntegerAttribute(tree,
+ SCREEN_SIZE_ATTR);
+ int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
+ SCREEN_DENSITY_ATTR);
if (screenSize > 0 && screenDensity > 0) {
if (!first) {
printf(",");
@@ -428,6 +361,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 +408,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 +421,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) {
@@ -468,7 +434,7 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool
}
} else if (depth == 2 && withinApduService) {
if (tag == "aid-group") {
- String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
+ String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
if (error != "") {
if (outError != NULL) *outError = error;
return Vector<String8>();
@@ -483,6 +449,126 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool
return categories;
}
+static void printComponentPresence(const char* componentName) {
+ printf("provides-component:'%s'\n", componentName);
+}
+
+/**
+ * Represents a feature that has been automatically added due to
+ * a pre-requisite or some other reason.
+ */
+struct ImpliedFeature {
+ /**
+ * Name of the implied feature.
+ */
+ String8 name;
+
+ /**
+ * List of human-readable reasons for why this feature was implied.
+ */
+ SortedVector<String8> reasons;
+};
+
+/**
+ * Represents a <feature-group> tag in the AndroidManifest.xml
+ */
+struct FeatureGroup {
+ FeatureGroup() : openGLESVersion(-1) {}
+
+ /**
+ * Human readable label
+ */
+ String8 label;
+
+ /**
+ * Explicit features defined in the group
+ */
+ KeyedVector<String8, bool> features;
+
+ /**
+ * OpenGL ES version required
+ */
+ int openGLESVersion;
+};
+
+static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
+ const char* name, const char* reason) {
+ String8 name8(name);
+ ssize_t idx = impliedFeatures->indexOfKey(name8);
+ if (idx < 0) {
+ idx = impliedFeatures->add(name8, ImpliedFeature());
+ impliedFeatures->editValueAt(idx).name = name8;
+ }
+ impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
+}
+
+static void printFeatureGroup(const FeatureGroup& grp,
+ const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
+ printf("feature-group: label='%s'\n", grp.label.string());
+
+ if (grp.openGLESVersion > 0) {
+ printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
+ }
+
+ const size_t numFeatures = grp.features.size();
+ for (size_t i = 0; i < numFeatures; i++) {
+ if (!grp.features[i]) {
+ continue;
+ }
+
+ const String8& featureName = grp.features.keyAt(i);
+ printf(" uses-feature: name='%s'\n",
+ ResTable::normalizeForOutput(featureName.string()).string());
+ }
+
+ const size_t numImpliedFeatures =
+ (impliedFeatures != NULL) ? impliedFeatures->size() : 0;
+ for (size_t i = 0; i < numImpliedFeatures; i++) {
+ const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i);
+ if (grp.features.indexOfKey(impliedFeature.name) >= 0) {
+ // The feature is explicitly set, no need to use implied
+ // definition.
+ continue;
+ }
+
+ String8 printableFeatureName(ResTable::normalizeForOutput(
+ impliedFeature.name.string()));
+ printf(" uses-feature: name='%s'\n", printableFeatureName.string());
+ printf(" uses-implied-feature: name='%s' reason='",
+ printableFeatureName.string());
+ const size_t numReasons = impliedFeature.reasons.size();
+ for (size_t j = 0; j < numReasons; j++) {
+ printf("%s", impliedFeature.reasons[j].string());
+ if (j + 2 < numReasons) {
+ printf(", ");
+ } else if (j + 1 < numReasons) {
+ printf(", and ");
+ }
+ }
+ printf("'\n");
+ }
+}
+
+static void addParentFeatures(FeatureGroup* grp, const String8& name) {
+ if (name == "android.hardware.camera.autofocus" ||
+ name == "android.hardware.camera.flash") {
+ grp->features.add(String8("android.hardware.camera"), true);
+ } else if (name == "android.hardware.location.gps" ||
+ name == "android.hardware.location.network") {
+ grp->features.add(String8("android.hardware.location"), true);
+ } else if (name == "android.hardware.touchscreen.multitouch") {
+ grp->features.add(String8("android.hardware.touchscreen"), true);
+ } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
+ grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
+ grp->features.add(String8("android.hardware.touchscreen"), true);
+ } else if (name == "android.hardware.opengles.aep") {
+ const int openGLESVersion31 = 0x00030001;
+ if (openGLESVersion31 > grp->openGLESVersion) {
+ grp->openGLESVersion = openGLESVersion31;
+ }
+ }
+}
+
/*
* Handle the "dump" command, to extract select data from an archive.
*/
@@ -490,7 +576,6 @@ extern char CONSOLE_DATA[2925]; // see EOF
int doDump(Bundle* bundle)
{
status_t result = UNKNOWN_ERROR;
- Asset* asset = NULL;
if (bundle->getFileSpecCount() < 1) {
fprintf(stderr, "ERROR: no dump option specified\n");
@@ -529,9 +614,21 @@ int doDump(Bundle* bundle)
config.screenWidthDp = 320;
config.screenHeightDp = 480;
config.smallestScreenWidthDp = 320;
+ config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
assets.setConfiguration(config);
const ResTable& res = assets.getResources(false);
+ if (res.getError() != NO_ERROR) {
+ fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
+ return 1;
+ }
+
+ // The dynamicRefTable can be null if there are no resources for this asset cookie.
+ // This fine.
+ const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
+
+ Asset* asset = NULL;
+
if (strcmp("resources", option) == 0) {
#ifndef HAVE_ANDROID_OS
res.print(bundle->getValues());
@@ -549,8 +646,8 @@ int doDump(Bundle* bundle)
for (int i=2; i<bundle->getFileSpecCount(); i++) {
const char* resname = bundle->getFileSpecEntry(i);
- ResXMLTree tree;
- asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
+ ResXMLTree tree(dynamicRefTable);
+ asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
if (asset == NULL) {
fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
goto bail;
@@ -576,13 +673,13 @@ int doDump(Bundle* bundle)
for (int i=2; i<bundle->getFileSpecCount(); i++) {
const char* resname = bundle->getFileSpecEntry(i);
- ResXMLTree tree;
- asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
+ asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
if (asset == NULL) {
fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
goto bail;
}
+ ResXMLTree tree(dynamicRefTable);
if (tree.setTo(asset->getBuffer(true),
asset->getLength()) != NO_ERROR) {
fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
@@ -594,14 +691,13 @@ int doDump(Bundle* bundle)
}
} else {
- ResXMLTree tree;
- asset = assets.openNonAsset("AndroidManifest.xml",
- Asset::ACCESS_BUFFER);
+ asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
if (asset == NULL) {
fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
goto bail;
}
+ ResXMLTree tree(dynamicRefTable);
if (tree.setTo(asset->getBuffer(true),
asset->getLength()) != NO_ERROR) {
fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
@@ -622,35 +718,39 @@ 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") {
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
goto bail;
}
- String8 pkg = getAttribute(tree, NULL, "package", NULL);
- printf("package: %s\n", pkg.string());
+ String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
+ printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
} else if (depth == 2 && tag == "permission") {
String8 error;
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
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);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
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,
+ AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
}
}
} else if (strcmp("badging", option) == 0) {
@@ -663,7 +763,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);
}
@@ -674,24 +776,35 @@ int doDump(Bundle* bundle)
bool withinActivity = false;
bool isMainActivity = false;
bool isLauncherActivity = false;
+ bool isLeanbackLauncherActivity = false;
bool isSearchable = false;
bool withinApplication = false;
bool withinSupportsInput = false;
+ bool withinFeatureGroup = false;
bool withinReceiver = false;
bool withinService = false;
+ bool withinProvider = false;
bool withinIntentFilter = false;
bool hasMainActivity = false;
bool hasOtherActivities = false;
bool hasOtherReceivers = false;
bool hasOtherServices = false;
+ bool hasIntentFilter = false;
+
bool hasWallpaperService = false;
bool hasImeService = false;
bool hasAccessibilityService = false;
bool hasPrintService = false;
bool hasWidgetReceivers = false;
bool hasDeviceAdminReceiver = false;
- bool hasIntentFilter = false;
bool hasPaymentService = false;
+ bool hasDocumentsProvider = false;
+ bool hasCameraActivity = false;
+ bool hasCameraSecureActivity = false;
+ bool hasLauncher = false;
+ bool hasNotificationListenerService = false;
+ bool hasDreamService = false;
+
bool actMainActivity = false;
bool actWidgetReceivers = false;
bool actDeviceAdminEnabled = false;
@@ -701,6 +814,12 @@ int doDump(Bundle* bundle)
bool actPrintService = false;
bool actHostApduService = false;
bool actOffHostApduService = false;
+ bool actDocumentsProvider = false;
+ bool actNotificationListenerService = false;
+ bool actDreamService = false;
+ bool actCamera = false;
+ bool actCameraSecure = false;
+ bool catLauncher = false;
bool hasMetaHostPaymentCategory = false;
bool hasMetaOffHostPaymentCategory = false;
@@ -711,6 +830,9 @@ int doDump(Bundle* bundle)
bool hasBindAccessibilityServicePermission = false;
bool hasBindPrintServicePermission = false;
bool hasBindNfcServicePermission = false;
+ bool hasRequiredSafAttributes = false;
+ bool hasBindNotificationListenerServicePermission = false;
+ bool hasBindDreamServicePermission = false;
// These two implement the implicit permissions that are granted
// to pre-1.6 applications.
@@ -726,41 +848,16 @@ int doDump(Bundle* bundle)
bool hasReadCallLogPermission = false;
bool hasWriteCallLogPermission = false;
+ // If an app declares itself as multiArch, we report the
+ // native libraries differently.
+ bool hasMultiArch = false;
+
// This next group of variables is used to implement a group of
// backward-compatibility heuristics necessitated by the addition of
// some new uses-feature constants in 2.1 and 2.2. In most cases, the
// heuristic is "if an app requests a permission but doesn't explicitly
// request the corresponding <uses-feature>, presume it's there anyway".
- bool specCameraFeature = false; // camera-related
- bool specCameraAutofocusFeature = false;
- bool reqCameraAutofocusFeature = false;
- bool reqCameraFlashFeature = false;
- bool hasCameraPermission = false;
- bool specLocationFeature = false; // location-related
- bool specNetworkLocFeature = false;
- bool reqNetworkLocFeature = false;
- bool specGpsFeature = false;
- bool reqGpsFeature = false;
- bool hasMockLocPermission = false;
- bool hasCoarseLocPermission = false;
- bool hasGpsPermission = false;
- bool hasGeneralLocPermission = false;
- bool specBluetoothFeature = false; // Bluetooth API-related
- bool hasBluetoothPermission = false;
- bool specMicrophoneFeature = false; // microphone-related
- bool hasRecordAudioPermission = false;
- bool specWiFiFeature = false;
- bool hasWiFiPermission = false;
- bool specTelephonyFeature = false; // telephony-related
- bool reqTelephonySubFeature = false;
- bool hasTelephonyPermission = false;
- bool specTouchscreenFeature = false; // touchscreen-related
- bool specMultitouchFeature = false;
- bool reqDistinctMultitouchFeature = false;
- bool specScreenPortraitFeature = false;
- bool specScreenLandscapeFeature = false;
- bool reqScreenPortraitFeature = false;
- bool reqScreenLandscapeFeature = false;
+
// 2.2 also added some other features that apps can request, but that
// have no corresponding permission, so we cannot implement any
// back-compatibility heuristic for them. The below are thus unnecessary
@@ -784,9 +881,15 @@ int doDump(Bundle* bundle)
String8 activityName;
String8 activityLabel;
String8 activityIcon;
+ String8 activityBanner;
String8 receiverName;
String8 serviceName;
Vector<String8> supportedInput;
+
+ FeatureGroup commonFeatures;
+ Vector<FeatureGroup> featureGroups;
+ KeyedVector<String8, ImpliedFeature> impliedFeatures;
+
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
depth--;
@@ -795,7 +898,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 {
@@ -806,16 +910,31 @@ int doDump(Bundle* bundle)
}
withinApplication = false;
withinSupportsInput = false;
+ withinFeatureGroup = false;
} else if (depth < 3) {
- if (withinActivity && isMainActivity && isLauncherActivity) {
- const char *aName = getComponentName(pkg, activityName);
- printf("launchable-activity:");
- if (aName != NULL) {
- printf(" name='%s' ", aName);
+ if (withinActivity && isMainActivity) {
+ String8 aName(getComponentName(pkg, activityName));
+ if (isLauncherActivity) {
+ printf("launchable-activity:");
+ if (aName.length() > 0) {
+ printf(" name='%s' ",
+ ResTable::normalizeForOutput(aName.string()).string());
+ }
+ printf(" label='%s' icon='%s'\n",
+ ResTable::normalizeForOutput(activityLabel.string()).string(),
+ ResTable::normalizeForOutput(activityIcon.string()).string());
+ }
+ if (isLeanbackLauncherActivity) {
+ printf("leanback-launchable-activity:");
+ if (aName.length() > 0) {
+ printf(" name='%s' ",
+ ResTable::normalizeForOutput(aName.string()).string());
+ }
+ printf(" label='%s' icon='%s' banner='%s'\n",
+ ResTable::normalizeForOutput(activityLabel.string()).string(),
+ ResTable::normalizeForOutput(activityIcon.string()).string(),
+ ResTable::normalizeForOutput(activityBanner.string()).string());
}
- printf(" label='%s' icon='%s'\n",
- activityLabel.string(),
- activityIcon.string());
}
if (!hasIntentFilter) {
hasOtherActivities |= withinActivity;
@@ -832,13 +951,17 @@ int doDump(Bundle* bundle)
withinActivity = false;
withinService = false;
withinReceiver = false;
+ withinProvider = false;
hasIntentFilter = false;
- isMainActivity = isLauncherActivity = false;
+ isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
} else if (depth < 4) {
if (withinIntentFilter) {
if (withinActivity) {
hasMainActivity |= actMainActivity;
- hasOtherActivities |= !actMainActivity;
+ hasLauncher |= catLauncher;
+ hasCameraActivity |= actCamera;
+ hasCameraSecureActivity |= actCameraSecure;
+ hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
} else if (withinReceiver) {
hasWidgetReceivers |= actWidgetReceivers;
hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
@@ -850,9 +973,15 @@ int doDump(Bundle* bundle)
hasAccessibilityService |= (actAccessibilityService &&
hasBindAccessibilityServicePermission);
hasPrintService |= (actPrintService && hasBindPrintServicePermission);
+ hasNotificationListenerService |= actNotificationListenerService &&
+ hasBindNotificationListenerServicePermission;
+ hasDreamService |= actDreamService && hasBindDreamServicePermission;
hasOtherServices |= (!actImeService && !actWallpaperService &&
!actAccessibilityService && !actPrintService &&
- !actHostApduService && !actOffHostApduService);
+ !actHostApduService && !actOffHostApduService &&
+ !actNotificationListenerService);
+ } else if (withinProvider) {
+ hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
}
}
withinIntentFilter = false;
@@ -863,18 +992,27 @@ 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") {
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
goto bail;
}
- pkg = getAttribute(tree, NULL, "package", NULL);
- printf("package: name='%s' ", pkg.string());
- int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+ pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
+ printf("package: name='%s' ",
+ ResTable::normalizeForOutput(pkg.string()).string());
+ int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
+ &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
+ error.string());
goto bail;
}
if (versionCode > 0) {
@@ -882,12 +1020,53 @@ int doDump(Bundle* bundle)
} else {
printf("versionCode='' ");
}
- String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
+ String8 versionName = AaptXml::getResolvedAttribute(res, tree,
+ VERSION_NAME_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
+ error.string());
goto bail;
}
- printf("versionName='%s'\n", versionName.string());
+ printf("versionName='%s'",
+ ResTable::normalizeForOutput(versionName.string()).string());
+
+ String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
+ if (!splitName.isEmpty()) {
+ printf(" split='%s'", ResTable::normalizeForOutput(
+ splitName.string()).string());
+ }
+
+ String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
+ "platformBuildVersionName");
+ printf(" platformBuildVersionName='%s'", platformVersionName.string());
+ printf("\n");
+
+ int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
+ INSTALL_LOCATION_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
+ error.string());
+ goto bail;
+ }
+
+ if (installLocation >= 0) {
+ printf("install-location:'");
+ switch (installLocation) {
+ case 0:
+ printf("auto");
+ break;
+ case 1:
+ printf("internalOnly");
+ break;
+ case 2:
+ printf("preferExternal");
+ break;
+ default:
+ fprintf(stderr, "Invalid installLocation %d\n", installLocation);
+ goto bail;
+ }
+ printf("'\n");
+ }
} else if (depth == 2) {
withinApplication = false;
if (tag == "application") {
@@ -898,17 +1077,19 @@ int doDump(Bundle* bundle)
for (size_t i=0; i<NL; i++) {
const char* localeStr = locales[i].string();
assets.setLocale(localeStr != NULL ? localeStr : "");
- String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+ &error);
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());
}
}
}
@@ -918,68 +1099,94 @@ int doDump(Bundle* bundle)
for (size_t i=0; i<ND; i++) {
tmpConfig.density = densities[i];
assets.setConfiguration(tmpConfig);
- String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ String8 icon = AaptXml::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);
- String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
+ error.string());
goto bail;
}
- int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
+ int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
+ &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
+ 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);
}
- int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
+ int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
+ DEBUGGABLE_ATTR, 0, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
+ error.string());
goto bail;
}
if (debuggable != 0) {
printf("application-debuggable\n");
}
+
+ // We must search by name because the multiArch flag hasn't been API
+ // frozen yet.
+ int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
+ "multiArch");
+ if (multiArchIndex >= 0) {
+ Res_value value;
+ if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) {
+ if (value.dataType >= Res_value::TYPE_FIRST_INT &&
+ value.dataType <= Res_value::TYPE_LAST_INT) {
+ hasMultiArch = value.data;
+ }
+ }
+ }
} else if (tag == "uses-sdk") {
- int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
+ int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
if (error != "") {
error = "";
- String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
+ String8 name = AaptXml::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());
+ printf("sdkVersion:'%s'\n",
+ ResTable::normalizeForOutput(name.string()).string());
} else if (code != -1) {
targetSdk = code;
printf("sdkVersion:'%d'\n", code);
}
- code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
+ code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
if (code != -1) {
printf("maxSdkVersion:'%d'\n", code);
}
- code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
+ code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
if (error != "") {
error = "";
- String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
+ String8 name = AaptXml::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());
+ printf("targetSdkVersion:'%s'\n",
+ ResTable::normalizeForOutput(name.string()).string());
} else if (code != -1) {
if (targetSdk < code) {
targetSdk = code;
@@ -987,16 +1194,16 @@ int doDump(Bundle* bundle)
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);
+ int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
+ REQ_TOUCH_SCREEN_ATTR, 0);
+ int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
+ REQ_KEYBOARD_TYPE_ATTR, 0);
+ int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
+ REQ_HARD_KEYBOARD_ATTR, 0);
+ int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
+ REQ_NAVIGATION_ATTR, 0);
+ int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
+ REQ_FIVE_WAY_NAV_ATTR, 0);
printf("uses-configuration:");
if (reqTouchScreen != 0) {
printf(" reqTouchScreen='%d'", reqTouchScreen);
@@ -1017,104 +1224,101 @@ int doDump(Bundle* bundle)
} else if (tag == "supports-input") {
withinSupportsInput = true;
} 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);
- xlargeScreen = getIntegerAttribute(tree,
- XLARGE_SCREEN_ATTR, NULL, 1);
- anyDensity = getIntegerAttribute(tree,
- ANY_DENSITY_ATTR, NULL, 1);
- requiresSmallestWidthDp = getIntegerAttribute(tree,
- REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
- compatibleWidthLimitDp = getIntegerAttribute(tree,
- COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
- largestWidthLimitDp = getIntegerAttribute(tree,
- LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
- } else if (tag == "uses-feature") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ smallScreen = AaptXml::getIntegerAttribute(tree,
+ SMALL_SCREEN_ATTR, 1);
+ normalScreen = AaptXml::getIntegerAttribute(tree,
+ NORMAL_SCREEN_ATTR, 1);
+ largeScreen = AaptXml::getIntegerAttribute(tree,
+ LARGE_SCREEN_ATTR, 1);
+ xlargeScreen = AaptXml::getIntegerAttribute(tree,
+ XLARGE_SCREEN_ATTR, 1);
+ anyDensity = AaptXml::getIntegerAttribute(tree,
+ ANY_DENSITY_ATTR, 1);
+ requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
+ REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
+ compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+ COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
+ largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+ LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
+ } else if (tag == "feature-group") {
+ withinFeatureGroup = true;
+ FeatureGroup group;
+ group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:label' attribute:"
+ " %s\n", error.string());
+ goto bail;
+ }
+ featureGroups.add(group);
+ } else if (tag == "uses-feature") {
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- int req = getIntegerAttribute(tree,
- REQUIRED_ATTR, NULL, 1);
-
- if (name == "android.hardware.camera") {
- specCameraFeature = true;
- } else if (name == "android.hardware.camera.autofocus") {
- // these have no corresponding permission to check for,
- // but should imply the foundational camera permission
- reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
- specCameraAutofocusFeature = true;
- } else if (req && (name == "android.hardware.camera.flash")) {
- // these have no corresponding permission to check for,
- // but should imply the foundational camera permission
- reqCameraFlashFeature = true;
- } else if (name == "android.hardware.location") {
- specLocationFeature = true;
- } else if (name == "android.hardware.location.network") {
- specNetworkLocFeature = true;
- reqNetworkLocFeature = reqNetworkLocFeature || req;
- } else if (name == "android.hardware.location.gps") {
- specGpsFeature = true;
- reqGpsFeature = reqGpsFeature || req;
- } else if (name == "android.hardware.bluetooth") {
- specBluetoothFeature = true;
- } else if (name == "android.hardware.touchscreen") {
- specTouchscreenFeature = true;
- } else if (name == "android.hardware.touchscreen.multitouch") {
- specMultitouchFeature = true;
- } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
- reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
- } else if (name == "android.hardware.microphone") {
- specMicrophoneFeature = true;
- } else if (name == "android.hardware.wifi") {
- specWiFiFeature = true;
- } else if (name == "android.hardware.telephony") {
- specTelephonyFeature = true;
- } else if (req && (name == "android.hardware.telephony.gsm" ||
- name == "android.hardware.telephony.cdma")) {
- // these have no corresponding permission to check for,
- // but should imply the foundational telephony permission
- reqTelephonySubFeature = true;
- } else if (name == "android.hardware.screen.portrait") {
- specScreenPortraitFeature = true;
- } else if (name == "android.hardware.screen.landscape") {
- specScreenLandscapeFeature = true;
+ int req = AaptXml::getIntegerAttribute(tree,
+ REQUIRED_ATTR, 1);
+
+ commonFeatures.features.add(name, req);
+ if (req) {
+ addParentFeatures(&commonFeatures, name);
}
- printf("uses-feature%s:'%s'\n",
- req ? "" : "-not-required", name.string());
} else {
- int vers = getIntegerAttribute(tree,
+ int vers = AaptXml::getIntegerAttribute(tree,
GL_ES_VERSION_ATTR, &error);
if (error == "") {
- printf("uses-gl-es:'0x%x'\n", vers);
+ if (vers > commonFeatures.openGLESVersion) {
+ commonFeatures.openGLESVersion = vers;
+ }
}
}
} else if (tag == "uses-permission") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
if (name == "android.permission.CAMERA") {
- hasCameraPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.camera",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_FINE_LOCATION") {
- hasGpsPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
+ String8::format("requested %s permission", name.string())
+ .string());
+ addImpliedFeature(&impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
- hasMockLocPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
- hasCoarseLocPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
+ String8::format("requested %s permission", name.string())
+ .string());
+ addImpliedFeature(&impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
name == "android.permission.INSTALL_LOCATION_PROVIDER") {
- hasGeneralLocPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.BLUETOOTH" ||
name == "android.permission.BLUETOOTH_ADMIN") {
- hasBluetoothPermission = true;
+ if (targetSdk > 4) {
+ addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
+ String8::format("requested %s permission", name.string())
+ .string());
+ addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
+ "targetSdkVersion > 4");
+ }
} else if (name == "android.permission.RECORD_AUDIO") {
- hasRecordAudioPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.ACCESS_WIFI_STATE" ||
name == "android.permission.CHANGE_WIFI_STATE" ||
name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
- hasWiFiPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
+ String8::format("requested %s permission", name.string())
+ .string());
} else if (name == "android.permission.CALL_PHONE" ||
name == "android.permission.CALL_PRIVILEGED" ||
name == "android.permission.MODIFY_PHONE_STATE" ||
@@ -1126,7 +1330,8 @@ int doDump(Bundle* bundle)
name == "android.permission.SEND_SMS" ||
name == "android.permission.WRITE_APN_SETTINGS" ||
name == "android.permission.WRITE_SMS") {
- hasTelephonyPermission = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
+ String8("requested a telephony permission").string());
} else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
hasWriteExternalStoragePermission = true;
} else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
@@ -1142,53 +1347,61 @@ 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,
+ AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
+ } else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
}
} else if (tag == "uses-package") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::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());
goto bail;
}
} else if (tag == "original-package") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::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());
goto bail;
}
} else if (tag == "supports-gl-texture") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::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);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+ String8 publicKey = AaptXml::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());
}
}
}
@@ -1196,6 +1409,7 @@ int doDump(Bundle* bundle)
withinActivity = false;
withinReceiver = false;
withinService = false;
+ withinProvider = false;
hasIntentFilter = false;
hasMetaHostPaymentCategory = false;
hasMetaOffHostPaymentCategory = false;
@@ -1204,56 +1418,72 @@ int doDump(Bundle* bundle)
hasBindAccessibilityServicePermission = false;
hasBindPrintServicePermission = false;
hasBindNfcServicePermission = false;
+ hasRequiredSafAttributes = false;
+ hasBindNotificationListenerServicePermission = false;
+ hasBindDreamServicePermission = false;
if (withinApplication) {
if(tag == "activity") {
withinActivity = true;
- activityName = getAttribute(tree, NAME_ATTR, &error);
+ activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
}
- activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
error.string());
goto bail;
}
- activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
error.string());
goto bail;
}
- int32_t orien = getResolvedIntegerAttribute(&res, tree,
+ activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
+ &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
+ error.string());
+ goto bail;
+ }
+
+ int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
SCREEN_ORIENTATION_ATTR, &error);
if (error == "") {
if (orien == 0 || orien == 6 || orien == 8) {
// Requests landscape, sensorLandscape, or reverseLandscape.
- reqScreenLandscapeFeature = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
+ "one or more activities have specified a landscape orientation");
} else if (orien == 1 || orien == 7 || orien == 9) {
// Requests portrait, sensorPortrait, or reversePortrait.
- reqScreenPortraitFeature = true;
+ addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
+ "one or more activities have specified a portrait orientation");
}
}
} else if (tag == "uses-library") {
- String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
+ String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr,
"ERROR getting 'android:name' attribute for uses-library"
" %s\n", error.string());
goto bail;
}
- int req = getIntegerAttribute(tree,
- REQUIRED_ATTR, NULL, 1);
+ int req = AaptXml::getIntegerAttribute(tree,
+ REQUIRED_ATTR, 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);
+ receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr,
@@ -1262,7 +1492,8 @@ int doDump(Bundle* bundle)
goto bail;
}
- String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+ String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+ &error);
if (error == "") {
if (permission == "android.permission.BIND_DEVICE_ADMIN") {
hasBindDeviceAdminPermission = true;
@@ -1273,15 +1504,16 @@ int doDump(Bundle* bundle)
}
} else if (tag == "service") {
withinService = true;
- serviceName = getAttribute(tree, NAME_ATTR, &error);
+ serviceName = AaptXml::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;
}
- String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+ String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+ &error);
if (error == "") {
if (permission == "android.permission.BIND_INPUT_METHOD") {
hasBindInputMethodPermission = true;
@@ -1291,20 +1523,95 @@ int doDump(Bundle* bundle)
hasBindPrintServicePermission = true;
} else if (permission == "android.permission.BIND_NFC_SERVICE") {
hasBindNfcServicePermission = true;
+ } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
+ hasBindNotificationListenerServicePermission = true;
+ } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
+ hasBindDreamServicePermission = true;
}
} else {
fprintf(stderr, "ERROR getting 'android:permission' attribute for"
" service '%s': %s\n", serviceName.string(), error.string());
}
+ } else if (tag == "provider") {
+ withinProvider = true;
+
+ bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
+ EXPORTED_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
+ " %s\n", error.string());
+ goto bail;
+ }
+
+ bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
+ res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
+ " %s\n", error.string());
+ goto bail;
+ }
+
+ String8 permission = AaptXml::getResolvedAttribute(res, tree,
+ PERMISSION_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
+ " %s\n", error.string());
+ goto bail;
+ }
+
+ hasRequiredSafAttributes |= exported && grantUriPermissions &&
+ permission == "android.permission.MANAGE_DOCUMENTS";
+
+ } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
+ String8 metaDataName = AaptXml::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 = AaptXml::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 (withinSupportsInput && tag == "input-type") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ } else if (withinFeatureGroup && tag == "uses-feature") {
+ FeatureGroup& top = featureGroups.editTop();
+
+ String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- supportedInput.add(name);
+ top.features.add(name, true);
+ addParentFeatures(&top, name);
} else {
- fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
- error.string());
- goto bail;
+ int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
+ &error);
+ if (error == "") {
+ if (vers > top.openGLESVersion) {
+ top.openGLESVersion = vers;
+ }
+ }
}
}
} else if (depth == 4) {
@@ -1320,8 +1627,14 @@ int doDump(Bundle* bundle)
actDeviceAdminEnabled = false;
actHostApduService = false;
actOffHostApduService = false;
+ actDocumentsProvider = false;
+ actNotificationListenerService = false;
+ actDreamService = false;
+ actCamera = false;
+ actCameraSecure = false;
+ catLauncher = false;
} else if (withinService && tag == "meta-data") {
- String8 name = getAttribute(tree, NAME_ATTR, &error);
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute for"
" meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1335,7 +1648,8 @@ int doDump(Bundle* bundle)
offHost = false;
}
- String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
+ String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
+ RESOURCE_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:resource' attribute for"
" meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1361,18 +1675,25 @@ 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);
+ action = AaptXml::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;
actMainActivity = true;
+ } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
+ action == "android.media.action.VIDEO_CAMERA") {
+ actCamera = true;
+ } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
+ actCameraSecure = true;
}
} else if (withinReceiver) {
if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
@@ -1393,6 +1714,14 @@ int doDump(Bundle* bundle)
actHostApduService = true;
} else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
actOffHostApduService = true;
+ } else if (action == "android.service.notification.NotificationListenerService") {
+ actNotificationListenerService = true;
+ } else if (action == "android.service.dreams.DreamService") {
+ actDreamService = true;
+ }
+ } else if (withinProvider) {
+ if (action == "android.content.action.DOCUMENTS_PROVIDER") {
+ actDocumentsProvider = true;
}
}
if (action == "android.intent.action.SEARCH") {
@@ -1401,14 +1730,19 @@ int doDump(Bundle* bundle)
}
if (tag == "category") {
- String8 category = getAttribute(tree, NAME_ATTR, &error);
+ String8 category = AaptXml::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) {
if (category == "android.intent.category.LAUNCHER") {
isLauncherActivity = true;
+ } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
+ isLeanbackLauncherActivity = true;
+ } else if (category == "android.intent.category.HOME") {
+ catLauncher = true;
}
}
}
@@ -1418,15 +1752,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"));
}
}
@@ -1435,188 +1769,109 @@ 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"));
}
}
- /* The following blocks handle printing "inferred" uses-features, based
- * on whether related features or permissions are used by the app.
- * Note that the various spec*Feature variables denote whether the
- * relevant tag was *present* in the AndroidManfest, not that it was
- * present and set to true.
- */
- // Camera-related back-compatibility logic
- if (!specCameraFeature) {
- if (reqCameraFlashFeature) {
- // if app requested a sub-feature (autofocus or flash) and didn't
- // request the base camera feature, we infer that it meant to
- printf("uses-feature:'android.hardware.camera'\n");
- printf("uses-implied-feature:'android.hardware.camera'," \
- "'requested android.hardware.camera.flash feature'\n");
- } else if (reqCameraAutofocusFeature) {
- // if app requested a sub-feature (autofocus or flash) and didn't
- // request the base camera feature, we infer that it meant to
- printf("uses-feature:'android.hardware.camera'\n");
- 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
- // 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");
- if (!specCameraAutofocusFeature) {
- printf("uses-feature:'android.hardware.camera.autofocus'\n");
- printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
- "'requested android.permission.CAMERA permission'\n");
- }
- }
- }
+ addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
+ "default feature for all apps");
- // Location-related back-compatibility logic
- if (!specLocationFeature &&
- (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
- hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
- // if app either takes a location-related permission or requests one of the
- // sub-features, we infer that it also meant to request the base location feature
- printf("uses-feature:'android.hardware.location'\n");
- printf("uses-implied-feature:'android.hardware.location'," \
- "'requested a location access permission'\n");
- }
- if (!specGpsFeature && hasGpsPermission) {
- // if app takes GPS (FINE location) perm but does not request the GPS
- // feature, we infer that it meant to
- printf("uses-feature:'android.hardware.location.gps'\n");
- printf("uses-implied-feature:'android.hardware.location.gps'," \
- "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
- }
- if (!specNetworkLocFeature && hasCoarseLocPermission) {
- // if app takes Network location (COARSE location) perm but does not request the
- // network location feature, we infer that it meant to
- printf("uses-feature:'android.hardware.location.network'\n");
- printf("uses-implied-feature:'android.hardware.location.network'," \
- "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
- }
-
- // Bluetooth-related compatibility logic
- if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
- // if app takes a Bluetooth permission but does not request the Bluetooth
- // feature, we infer that it meant to
- printf("uses-feature:'android.hardware.bluetooth'\n");
- printf("uses-implied-feature:'android.hardware.bluetooth'," \
- "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
- "permission and targetSdkVersion > 4'\n");
- }
-
- // Microphone-related compatibility logic
- if (!specMicrophoneFeature && hasRecordAudioPermission) {
- // if app takes the record-audio permission but does not request the microphone
- // feature, we infer that it meant to
- printf("uses-feature:'android.hardware.microphone'\n");
- printf("uses-implied-feature:'android.hardware.microphone'," \
- "'requested android.permission.RECORD_AUDIO permission'\n");
- }
+ const size_t numFeatureGroups = featureGroups.size();
+ if (numFeatureGroups == 0) {
+ // If no <feature-group> tags were defined, apply auto-implied features.
+ printFeatureGroup(commonFeatures, &impliedFeatures);
- // WiFi-related compatibility logic
- if (!specWiFiFeature && hasWiFiPermission) {
- // if app takes one of the WiFi permissions but does not request the WiFi
- // feature, we infer that it meant to
- printf("uses-feature:'android.hardware.wifi'\n");
- printf("uses-implied-feature:'android.hardware.wifi'," \
- "'requested android.permission.ACCESS_WIFI_STATE, " \
- "android.permission.CHANGE_WIFI_STATE, or " \
- "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
- }
+ } else {
+ // <feature-group> tags are defined, so we ignore implied features and
+ for (size_t i = 0; i < numFeatureGroups; i++) {
+ FeatureGroup& grp = featureGroups.editItemAt(i);
- // Telephony-related compatibility logic
- if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
- // if app takes one of the telephony permissions or requests a sub-feature but
- // does not request the base telephony feature, we infer that it meant to
- printf("uses-feature:'android.hardware.telephony'\n");
- printf("uses-implied-feature:'android.hardware.telephony'," \
- "'requested a telephony-related permission or feature'\n");
- }
+ if (commonFeatures.openGLESVersion > grp.openGLESVersion) {
+ grp.openGLESVersion = commonFeatures.openGLESVersion;
+ }
- // Touchscreen-related back-compatibility logic
- if (!specTouchscreenFeature) { // not a typo!
- // all apps are presumed to require a touchscreen, unless they explicitly say
- // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
- // Note that specTouchscreenFeature is true if the tag is present, regardless
- // of whether its value is true or false, so this is safe
- printf("uses-feature:'android.hardware.touchscreen'\n");
- printf("uses-implied-feature:'android.hardware.touchscreen'," \
- "'assumed you require a touch screen unless explicitly made optional'\n");
- }
- if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
- // if app takes one of the telephony permissions or requests a sub-feature but
- // does not request the base telephony feature, we infer that it meant to
- printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
- printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
- "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
- }
+ // Merge the features defined in the top level (not inside a <feature-group>)
+ // with this feature group.
+ const size_t numCommonFeatures = commonFeatures.features.size();
+ for (size_t j = 0; j < numCommonFeatures; j++) {
+ if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) {
+ grp.features.add(commonFeatures.features.keyAt(j),
+ commonFeatures.features[j]);
+ }
+ }
- // Landscape/portrait-related compatibility logic
- if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
- // If the app has specified any activities in its manifest
- // that request a specific orientation, then assume that
- // orientation is required.
- if (reqScreenLandscapeFeature) {
- printf("uses-feature:'android.hardware.screen.landscape'\n");
- printf("uses-implied-feature:'android.hardware.screen.landscape'," \
- "'one or more activities have specified a landscape orientation'\n");
- }
- if (reqScreenPortraitFeature) {
- printf("uses-feature:'android.hardware.screen.portrait'\n");
- printf("uses-implied-feature:'android.hardware.screen.portrait'," \
- "'one or more activities have specified a portrait orientation'\n");
+ if (!grp.features.isEmpty()) {
+ printFeatureGroup(grp);
+ }
}
}
- if (hasMainActivity) {
- printf("main\n");
- }
+
if (hasWidgetReceivers) {
- printf("app-widget\n");
+ printComponentPresence("app-widget");
}
if (hasDeviceAdminReceiver) {
- printf("device-admin\n");
+ printComponentPresence("device-admin");
}
if (hasImeService) {
- printf("ime\n");
+ printComponentPresence("ime");
}
if (hasWallpaperService) {
- printf("wallpaper\n");
+ printComponentPresence("wallpaper");
}
if (hasAccessibilityService) {
- printf("accessibility\n");
+ printComponentPresence("accessibility");
}
if (hasPrintService) {
- printf("print\n");
+ printComponentPresence("print-service");
}
if (hasPaymentService) {
- printf("payment\n");
+ printComponentPresence("payment");
+ }
+ if (isSearchable) {
+ printComponentPresence("search");
+ }
+ if (hasDocumentsProvider) {
+ printComponentPresence("document-provider");
+ }
+ if (hasLauncher) {
+ printComponentPresence("launcher");
+ }
+ if (hasNotificationListenerService) {
+ printComponentPresence("notification-listener");
+ }
+ if (hasDreamService) {
+ printComponentPresence("dream");
+ }
+ if (hasCameraActivity) {
+ printComponentPresence("camera");
+ }
+ if (hasCameraSecureActivity) {
+ printComponentPresence("camera-secure");
+ }
+
+ if (hasMainActivity) {
+ printf("main\n");
}
if (hasOtherActivities) {
printf("other-activities\n");
}
- if (isSearchable) {
- printf("search\n");
- }
- if (hasOtherReceivers) {
+ if (hasOtherReceivers) {
printf("other-receivers\n");
}
if (hasOtherServices) {
@@ -1628,7 +1883,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 {
@@ -1673,10 +1930,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) {
@@ -1710,11 +1975,54 @@ int doDump(Bundle* bundle)
AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
if (dir != NULL) {
if (dir->getFileCount() > 0) {
- printf("native-code:");
+ SortedVector<String8> architectures;
for (size_t i=0; i<dir->getFileCount(); i++) {
- printf(" '%s'", dir->getFileName(i).string());
+ architectures.add(ResTable::normalizeForOutput(
+ dir->getFileName(i).string()));
+ }
+
+ bool outputAltNativeCode = false;
+ // A multiArch package is one that contains 64-bit and
+ // 32-bit versions of native code and expects 3rd-party
+ // apps to load these native code libraries. Since most
+ // 64-bit systems also support 32-bit apps, the apps
+ // loading this multiArch package's code may be either
+ // 32-bit or 64-bit.
+ if (hasMultiArch) {
+ // If this is a multiArch package, report the 64-bit
+ // version only. Then as a separate entry, report the
+ // rest.
+ //
+ // If we report the 32-bit architecture, this APK will
+ // be installed on a 32-bit device, causing a large waste
+ // of bandwidth and disk space. This assumes that
+ // the developer of the multiArch package has also
+ // made a version that is 32-bit only.
+ String8 intel64("x86_64");
+ String8 arm64("arm64-v8a");
+ ssize_t index = architectures.indexOf(intel64);
+ if (index < 0) {
+ index = architectures.indexOf(arm64);
+ }
+
+ if (index >= 0) {
+ printf("native-code: '%s'\n", architectures[index].string());
+ architectures.removeAt(index);
+ outputAltNativeCode = true;
+ }
+ }
+
+ const size_t archCount = architectures.size();
+ if (archCount > 0) {
+ if (outputAltNativeCode) {
+ printf("alt-");
+ }
+ printf("native-code:");
+ for (size_t i = 0; i < archCount; i++) {
+ printf(" '%s'", architectures[i].string());
+ }
+ printf("\n");
}
- printf("\n");
}
delete dir;
}
@@ -1785,7 +2093,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 {
@@ -1795,12 +2104,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;
}
}
@@ -1867,6 +2177,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.
@@ -1880,23 +2242,24 @@ 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);
}
N = bundle->getFileSpecCount();
if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
- && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
+ && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
fprintf(stderr, "ERROR: no input files\n");
goto bail;
}
@@ -1938,9 +2301,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;
}
@@ -1983,10 +2369,12 @@ int doPackage(Bundle* bundle)
if (bundle->getCustomPackage() == NULL) {
// Write the R.java file into the appropriate class directory
// e.g. gen/com/foo/app/R.java
- err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
+ err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
+ bundle->getBuildSharedLibrary());
} else {
const String8 customPkg(bundle->getCustomPackage());
- err = writeResourceSymbols(bundle, assets, customPkg, true);
+ err = writeResourceSymbols(bundle, assets, customPkg, true,
+ bundle->getBuildSharedLibrary());
}
if (err < 0) {
goto bail;
@@ -2000,7 +2388,8 @@ int doPackage(Bundle* bundle)
char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
while (packageString != NULL) {
// Write the R.java file out with the correct package name
- err = writeResourceSymbols(bundle, assets, String8(packageString), true);
+ err = writeResourceSymbols(bundle, assets, String8(packageString), true,
+ bundle->getBuildSharedLibrary());
if (err < 0) {
goto bail;
}
@@ -2009,11 +2398,11 @@ int doPackage(Bundle* bundle)
libs.unlockBuffer();
}
} else {
- err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
+ err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false);
if (err < 0) {
goto bail;
}
- err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
+ err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false);
if (err < 0) {
goto bail;
}
@@ -2027,11 +2416,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.
@@ -2068,7 +2470,7 @@ bail:
*
* POSTCONDITIONS
* Destination directory will be updated to match the PNG files in
- * the source directory.
+ * the source directory.
*/
int doCrunch(Bundle* bundle)
{