Split ManifestExtractor to Extract and Print methods.

Bug: b/228950123
Test: Dump_test.cpp
Change-Id: I040224f4997d2904e899211898e59e9fc4d58f82
This commit is contained in:
Iurii Makhno
2022-04-12 15:04:12 +00:00
parent dcead62a5c
commit a9c74c5378

View File

@@ -17,17 +17,19 @@
#include "DumpManifest.h"
#include <algorithm>
#include <memory>
#include <set>
#include <vector>
#include "LoadedApk.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
#include "androidfw/ConfigDescription.h"
#include "io/File.h"
#include "io/FileStream.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
#include "androidfw/ConfigDescription.h"
using ::android::base::StringPrintf;
using ::android::ConfigDescription;
@@ -113,6 +115,9 @@ static xml::Attribute* FindAttribute(xml::Element *el, const std::string &packag
}
class CommonFeatureGroup;
class Architectures;
class SupportsScreen;
class FeatureGroup;
class ManifestExtractor {
public:
@@ -338,7 +343,8 @@ class ManifestExtractor {
return config;
}
bool Dump(text::Printer* printer, IDiagnostics* diag);
bool Extract(IDiagnostics* diag);
bool Dump(text::Printer* printer);
/** Recursively visit the xml element tree and return a processed badging element tree. */
std::unique_ptr<Element> Visit(xml::Element* element);
@@ -354,7 +360,7 @@ class ManifestExtractor {
* Retrieves the default feature group that features are added into when <uses-feature>
* are not in a <feature-group> element.
**/
CommonFeatureGroup* GetCommonFeatureGroup() {
CommonFeatureGroup* common_feature_group() {
return commonFeatureGroup_.get();
}
@@ -387,11 +393,22 @@ class ManifestExtractor {
DumpManifestOptions& options_;
private:
std::unique_ptr<xml::XmlResource> doc_;
std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_ = util::make_unique<CommonFeatureGroup>();
std::map<std::string, ConfigDescription> locales_;
std::map<uint16_t, ConfigDescription> densities_;
std::vector<Element*> parent_stack_;
int32_t target_sdk_ = 0;
std::unique_ptr<ManifestExtractor::Element> root_element_;
std::vector<std::unique_ptr<ManifestExtractor::Element>> implied_permissions_;
std::set<std::string> components_;
std::vector<FeatureGroup*> feature_groups_;
bool other_activities_ = false;
bool other_receivers_ = false;
bool other_services_ = false;
std::unique_ptr<Architectures> architectures_ = util::make_unique<Architectures>();
const SupportsScreen* supports_screen_;
};
template<typename T> T* ElementCast(ManifestExtractor::Element* element);
@@ -427,6 +444,7 @@ static ManifestExtractor::Element* FindElement(ManifestExtractor::Element* root,
class Manifest : public ManifestExtractor::Element {
public:
Manifest() = default;
bool only_package_name;
std::string package;
int32_t versionCode;
std::string versionName;
@@ -463,6 +481,14 @@ class Manifest : public ManifestExtractor::Element {
}
void Print(text::Printer* printer) override {
if (only_package_name) {
printer->Println(StringPrintf("package: %s", package.data()));
} else {
PrintFull(printer);
}
}
void PrintFull(text::Printer* printer) {
printer->Print(StringPrintf("package: name='%s' ", package.data()));
printer->Print(StringPrintf("versionCode='%s' ",
(versionCode > 0) ? std::to_string(versionCode).data() : ""));
@@ -733,7 +759,7 @@ class SupportsScreen : public ManifestExtractor::Element {
}
}
void PrintScreens(text::Printer* printer, int32_t target_sdk) {
void PrintScreens(text::Printer* printer, int32_t target_sdk) const {
int32_t small_screen_temp = small_screen;
int32_t normal_screen_temp = normal_screen;
int32_t large_screen_temp = large_screen;
@@ -1050,7 +1076,7 @@ class UsesFeature : public ManifestExtractor::Element {
// common feature group
FeatureGroup* feature_group = ElementCast<FeatureGroup>(extractor()->parent_stack()[0]);
if (!feature_group) {
feature_group = extractor()->GetCommonFeatureGroup();
feature_group = extractor()->common_feature_group();
} else {
// All features in side of <feature-group> elements are required.
required = true;
@@ -1068,12 +1094,14 @@ class UsesFeature : public ManifestExtractor::Element {
class UsesPermission : public ManifestExtractor::Element {
public:
UsesPermission() = default;
bool implied;
std::string name;
std::vector<std::string> requiredFeatures;
std::vector<std::string> requiredNotFeatures;
int32_t required = true;
int32_t maxSdkVersion = -1;
int32_t usesPermissionFlags = 0;
std::string impliedReason;
void Extract(xml::Element* element) override {
name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
@@ -1094,7 +1122,7 @@ class UsesPermission : public ManifestExtractor::Element {
FindAttribute(element, USES_PERMISSION_FLAGS_ATTR), 0);
if (!name.empty()) {
CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
CommonFeatureGroup* common = extractor()->common_feature_group();
common->addImpliedFeaturesForPermission(extractor()->target_sdk(), name, false);
}
}
@@ -1126,17 +1154,16 @@ class UsesPermission : public ManifestExtractor::Element {
printer->Print("\n");
}
}
}
void PrintImplied(text::Printer* printer, const std::string& reason) {
printer->Print(StringPrintf("uses-implied-permission: name='%s'", name.data()));
if (maxSdkVersion >= 0) {
printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
if (implied) {
printer->Print(StringPrintf("uses-implied-permission: name='%s'", name.data()));
if (maxSdkVersion >= 0) {
printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
}
if ((usesPermissionFlags & kNeverForLocation) != 0) {
printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
}
printer->Print(StringPrintf(" reason='%s'\n", impliedReason.data()));
}
if ((usesPermissionFlags & kNeverForLocation) != 0) {
printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
}
printer->Print(StringPrintf(" reason='%s'\n", reason.data()));
}
};
@@ -1184,7 +1211,7 @@ class UsesPermissionSdk23 : public ManifestExtractor::Element {
maxSdkVersion = GetAttributeInteger(FindAttribute(element, MAX_SDK_VERSION_ATTR));
if (name) {
CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
CommonFeatureGroup* common = extractor()->common_feature_group();
common->addImpliedFeaturesForPermission(extractor()->target_sdk(), *name, true);
}
}
@@ -1256,7 +1283,7 @@ class Activity : public ManifestExtractor::Element {
auto orientation = GetAttributeInteger(FindAttribute(element, SCREEN_ORIENTATION_ATTR));
if (orientation) {
CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
CommonFeatureGroup* common = extractor()->common_feature_group();
int orien = *orientation;
if (orien == 0 || orien == 6 || orien == 8) {
// Requests landscape, sensorLandscape, or reverseLandscape.
@@ -1962,6 +1989,29 @@ class Property : public ManifestExtractor::Element {
}
};
class Architectures {
public:
std::set<std::string> architectures;
std::set<std::string> alt_architectures;
void Print(text::Printer* printer) {
if (!architectures.empty()) {
printer->Print("native-code:");
for (auto& arch : architectures) {
printer->Print(StringPrintf(" '%s'", arch.data()));
}
printer->Print("\n");
}
if (!alt_architectures.empty()) {
printer->Print("alt-native-code:");
for (auto& arch : alt_architectures) {
printer->Print(StringPrintf(" '%s'", arch.data()));
}
printer->Print("\n");
}
}
};
/** Recursively prints the extracted badging element. */
static void Print(ManifestExtractor::Element* el, text::Printer* printer) {
el->Print(printer);
@@ -1970,15 +2020,15 @@ static void Print(ManifestExtractor::Element* el, text::Printer* printer) {
}
}
bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
bool ManifestExtractor::Extract(IDiagnostics* diag) {
// Load the manifest
std::unique_ptr<xml::XmlResource> doc = apk_->LoadXml("AndroidManifest.xml", diag);
if (doc == nullptr) {
doc_ = apk_->LoadXml("AndroidManifest.xml", diag);
if (doc_ == nullptr) {
diag->Error(DiagMessage() << "failed to find AndroidManifest.xml");
return false;
}
xml::Element* element = doc->root.get();
xml::Element* element = doc_->root.get();
if (element->name != "manifest") {
diag->Error(DiagMessage() << "manifest does not start with <manifest> tag");
return false;
@@ -1987,10 +2037,11 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
// Print only the <uses-permission>, <uses-permission-sdk23>, and <permission> elements if
// printing only permission elements is requested
if (options_.only_permissions) {
std::unique_ptr<ManifestExtractor::Element> manifest_element =
ManifestExtractor::Element::Inflate(this, element);
root_element_ = ManifestExtractor::Element::Inflate(this, element);
if (auto manifest = ElementCast<Manifest>(root_element_.get())) {
manifest->only_package_name = true;
if (auto manifest = ElementCast<Manifest>(manifest_element.get())) {
for (xml::Element* child : element->GetChildElements()) {
if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
|| child->name == "permission") {
@@ -1999,15 +2050,8 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
manifest->AddChild(permission_element);
}
}
printer->Print(StringPrintf("package: %s\n", manifest->package.data()));
ForEachChild(manifest, [&printer](ManifestExtractor::Element* el) -> void {
el->Print(printer);
});
return true;
}
return false;
}
@@ -2041,27 +2085,24 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
}
// Extract badging information
auto root = Visit(element);
root_element_ = Visit(element);
// Filter out all "uses-sdk" tags besides the very last tag. The android runtime only uses the
// attribute values from the last defined tag.
std::vector<UsesSdkBadging*> filtered_uses_sdk_tags;
for (const auto& child : root->children()) {
for (const auto& child : root_element_->children()) {
if (auto uses_sdk = ElementCast<UsesSdkBadging>(child.get())) {
filtered_uses_sdk_tags.emplace_back(uses_sdk);
}
}
if (filtered_uses_sdk_tags.size() >= 2U) {
filtered_uses_sdk_tags.pop_back();
root->Filter([&](const ManifestExtractor::Element* e) {
root_element_->Filter([&](const ManifestExtractor::Element* e) {
return std::find(filtered_uses_sdk_tags.begin(), filtered_uses_sdk_tags.end(), e) !=
filtered_uses_sdk_tags.end();
});
}
// Print the elements in order seen
Print(root.get(), printer);
/** Recursively checks the extracted elements for the specified permission. **/
auto FindPermission = [&](ManifestExtractor::Element* root,
const std::string& name) -> ManifestExtractor::Element* {
@@ -2073,30 +2114,30 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
});
};
auto PrintPermission = [&printer](const std::string& name, const std::string& reason,
int32_t max_sdk_version) -> void {
auto AddImpliedPermission = [&](const std::string& name, const std::string& reason,
int32_t max_sdk_version) -> void {
auto permission = util::make_unique<UsesPermission>();
permission->name = name;
permission->maxSdkVersion = max_sdk_version;
permission->Print(printer);
permission->PrintImplied(printer, reason);
permission->implied = true;
permission->impliedReason = reason;
implied_permissions_.push_back(std::move(permission));
};
// Implied permissions
// Pre-1.6 implicitly granted permission compatibility logic
CommonFeatureGroup* common_feature_group = GetCommonFeatureGroup();
bool insert_write_external = false;
auto write_external_permission = ElementCast<UsesPermission>(
FindPermission(root.get(), "android.permission.WRITE_EXTERNAL_STORAGE"));
FindPermission(root_element_.get(), "android.permission.WRITE_EXTERNAL_STORAGE"));
if (target_sdk() < SDK_DONUT) {
if (!write_external_permission) {
PrintPermission("android.permission.WRITE_EXTERNAL_STORAGE", "targetSdkVersion < 4", -1);
AddImpliedPermission("android.permission.WRITE_EXTERNAL_STORAGE", "targetSdkVersion < 4", -1);
insert_write_external = true;
}
if (!FindPermission(root.get(), "android.permission.READ_PHONE_STATE")) {
PrintPermission("android.permission.READ_PHONE_STATE", "targetSdkVersion < 4", -1);
if (!FindPermission(root_element_.get(), "android.permission.READ_PHONE_STATE")) {
AddImpliedPermission("android.permission.READ_PHONE_STATE", "targetSdkVersion < 4", -1);
}
}
@@ -2104,62 +2145,60 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
// force them to always take READ_EXTERNAL_STORAGE as well. We always
// do this (regardless of target API version) because we can't have
// an app with write permission but not read permission.
auto read_external = FindPermission(root.get(), "android.permission.READ_EXTERNAL_STORAGE");
auto read_external =
FindPermission(root_element_.get(), "android.permission.READ_EXTERNAL_STORAGE");
if (!read_external && (insert_write_external || write_external_permission)) {
PrintPermission("android.permission.READ_EXTERNAL_STORAGE",
"requested WRITE_EXTERNAL_STORAGE",
(write_external_permission) ? write_external_permission->maxSdkVersion : -1);
AddImpliedPermission(
"android.permission.READ_EXTERNAL_STORAGE", "requested WRITE_EXTERNAL_STORAGE",
(write_external_permission) ? write_external_permission->maxSdkVersion : -1);
}
// Pre-JellyBean call log permission compatibility.
if (target_sdk() < SDK_JELLY_BEAN) {
if (!FindPermission(root.get(), "android.permission.READ_CALL_LOG")
&& FindPermission(root.get(), "android.permission.READ_CONTACTS")) {
PrintPermission("android.permission.READ_CALL_LOG",
"targetSdkVersion < 16 and requested READ_CONTACTS", -1);
if (!FindPermission(root_element_.get(), "android.permission.READ_CALL_LOG") &&
FindPermission(root_element_.get(), "android.permission.READ_CONTACTS")) {
AddImpliedPermission("android.permission.READ_CALL_LOG",
"targetSdkVersion < 16 and requested READ_CONTACTS", -1);
}
if (!FindPermission(root.get(), "android.permission.WRITE_CALL_LOG")
&& FindPermission(root.get(), "android.permission.WRITE_CONTACTS")) {
PrintPermission("android.permission.WRITE_CALL_LOG",
"targetSdkVersion < 16 and requested WRITE_CONTACTS", -1);
if (!FindPermission(root_element_.get(), "android.permission.WRITE_CALL_LOG") &&
FindPermission(root_element_.get(), "android.permission.WRITE_CONTACTS")) {
AddImpliedPermission("android.permission.WRITE_CALL_LOG",
"targetSdkVersion < 16 and requested WRITE_CONTACTS", -1);
}
}
// If the app hasn't declared the touchscreen as a feature requirement (either
// directly or implied, required or not), then the faketouch feature is implied.
if (!common_feature_group->HasFeature("android.hardware.touchscreen")) {
common_feature_group->addImpliedFeature("android.hardware.faketouch",
"default feature for all apps", false);
if (!common_feature_group()->HasFeature("android.hardware.touchscreen")) {
common_feature_group()->addImpliedFeature("android.hardware.faketouch",
"default feature for all apps", false);
}
// Only print the common feature group if no feature group is defined
std::vector<FeatureGroup*> feature_groups;
ForEachChild(root.get(), [&feature_groups](ManifestExtractor::Element* el) -> void {
ForEachChild(root_element_.get(), [&feature_groups](ManifestExtractor::Element* el) -> void {
if (auto feature_group = ElementCast<FeatureGroup>(el)) {
feature_groups.push_back(feature_group);
}
});
if (feature_groups.empty()) {
common_feature_group->PrintGroup(printer);
feature_groups_.push_back(common_feature_group());
} else {
// Merge the common feature group into the feature group
for (auto& feature_group : feature_groups) {
feature_group->open_gles_version = std::max(feature_group->open_gles_version,
common_feature_group->open_gles_version);
feature_group->Merge(common_feature_group);
feature_group->PrintGroup(printer);
feature_group->Merge(common_feature_group());
feature_groups_.push_back(feature_group);
}
};
// Collect the component types of the application
std::set<std::string> components;
ForEachChild(root.get(), [&components](ManifestExtractor::Element* el) -> void {
ForEachChild(root_element_.get(), [&](ManifestExtractor::Element* el) -> void {
if (ElementCast<Action>(el)) {
auto action = ElementCast<Action>(el);
if (!action->component.empty()) {
components.insert(action->component);
components_.insert(action->component);
return;
}
}
@@ -2167,15 +2206,14 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
if (ElementCast<Category>(el)) {
auto category = ElementCast<Category>(el);
if (!category->component.empty()) {
components.insert(category->component);
components_.insert(category->component);
return;
}
}
});
// Check for the payment component
auto apk = apk_;
ForEachChild(root.get(), [&apk, &components, &diag](ManifestExtractor::Element* el) -> void {
ForEachChild(root_element_.get(), [this, &diag](ManifestExtractor::Element* el) -> void {
if (auto service = ElementCast<Service>(el)) {
auto host_apdu_action = ElementCast<Action>(FindElement(service,
[&](ManifestExtractor::Element* el) -> bool {
@@ -2193,44 +2231,162 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
return false;
}));
ForEachChild(service, [&apk, &components, &diag, &host_apdu_action,
&offhost_apdu_action](ManifestExtractor::Element* el) -> void {
if (auto meta_data = ElementCast<MetaData>(el)) {
if ((meta_data->name == "android.nfc.cardemulation.host_apdu_service" && host_apdu_action)
|| (meta_data->name == "android.nfc.cardemulation.off_host_apdu_service"
&& offhost_apdu_action)) {
ForEachChild(service,
[this, &diag, &host_apdu_action,
&offhost_apdu_action](ManifestExtractor::Element* el) -> void {
if (auto meta_data = ElementCast<MetaData>(el)) {
if ((meta_data->name == "android.nfc.cardemulation.host_apdu_service" &&
host_apdu_action) ||
(meta_data->name == "android.nfc.cardemulation.off_host_apdu_service" &&
offhost_apdu_action)) {
// Attempt to load the resource file
if (!meta_data->resource.empty()) {
return;
}
auto resource = this->apk_->LoadXml(meta_data->resource, diag);
if (!resource) {
return;
}
// Attempt to load the resource file
if (!meta_data->resource.empty()) {
return;
}
auto resource = apk->LoadXml(meta_data->resource, diag);
if (!resource) {
return;
}
// Look for the payment category on an <aid-group> element
auto& root = resource.get()->root;
if ((host_apdu_action && root->name == "host-apdu-service")
|| (offhost_apdu_action && root->name == "offhost-apdu-service")) {
for (auto& child : root->GetChildElements()) {
if (child->name == "aid-group") {
auto category = FindAttribute(child, CATEGORY_ATTR);
if (category && category->value == "payment") {
components.insert("payment");
return;
}
}
}
}
}
}
});
// Look for the payment category on an <aid-group> element
auto& root = resource.get()->root;
if ((host_apdu_action && root->name == "host-apdu-service") ||
(offhost_apdu_action && root->name == "offhost-apdu-service")) {
for (auto& child : root->GetChildElements()) {
if (child->name == "aid-group") {
auto category = FindAttribute(child, CATEGORY_ATTR);
if (category && category->value == "payment") {
this->components_.insert("payment");
return;
}
}
}
}
}
}
});
}
});
// Print presence of activities, receivers, and services with no special components
FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
if (auto activity = ElementCast<Activity>(el)) {
if (!activity->has_component_) {
other_activities_ = true;
return true;
}
}
return false;
});
FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
if (auto receiver = ElementCast<Receiver>(el)) {
if (!receiver->has_component) {
other_receivers_ = true;
return true;
}
}
return false;
});
FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
if (auto service = ElementCast<Service>(el)) {
if (!service->has_component) {
other_services_ = true;
return true;
}
}
return false;
});
// Gather the supported screens
const static SupportsScreen default_screens{};
SupportsScreen* screen = ElementCast<SupportsScreen>(
FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
return ElementCast<SupportsScreen>(el) != nullptr;
}));
supports_screen_ = screen ? screen : &default_screens;
// Gather the supported architectures_ of the app
std::set<std::string> architectures_from_apk;
auto it = apk_->GetFileCollection()->Iterator();
while (it->HasNext()) {
auto file_path = it->Next()->GetSource().path;
size_t pos = file_path.find("lib/");
if (pos != std::string::npos) {
file_path = file_path.substr(pos + 4);
pos = file_path.find('/');
if (pos != std::string::npos) {
file_path = file_path.substr(0, pos);
}
architectures_from_apk.insert(file_path);
}
}
// Determine if the application has multiArch supports
auto has_multi_arch =
FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
if (auto application = ElementCast<Application>(el)) {
return application->has_multi_arch;
}
return false;
});
bool output_alt_native_code = 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
if (has_multi_arch) {
// 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.
const std::string kIntel64 = "x86_64";
const std::string kArm64 = "arm64-v8a";
auto arch = architectures_from_apk.find(kIntel64);
if (arch == architectures_from_apk.end()) {
arch = architectures_from_apk.find(kArm64);
}
if (arch != architectures_from_apk.end()) {
architectures_->architectures.insert(*arch);
architectures_from_apk.erase(arch);
output_alt_native_code = true;
}
}
for (auto& arch : architectures_from_apk) {
if (output_alt_native_code) {
architectures_->alt_architectures.insert(arch);
} else {
architectures_->architectures.insert(arch);
}
}
return true;
}
bool ManifestExtractor::Dump(text::Printer* printer) {
Print(root_element_.get(), printer);
if (options_.only_permissions) {
return true;
}
for (auto& implied_permission : implied_permissions_) {
implied_permission->Print(printer);
}
for (auto& feature_group : feature_groups_) {
feature_group->PrintGroup(printer);
}
// Print the components types if they are present
auto& components = components_;
auto PrintComponent = [&components, &printer](const std::string& component) -> void {
if (components.find(component) != components.end()) {
printer->Print(StringPrintf("provides-component:'%s'\n", component.data()));
@@ -2257,50 +2413,16 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
printer->Print("main\n");
}
// Print presence of activities, recivers, and services with no special components
FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
if (auto activity = ElementCast<Activity>(el)) {
if (!activity->has_component_) {
printer->Print("other-activities\n");
return true;
}
}
return false;
});
FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
if (auto receiver = ElementCast<Receiver>(el)) {
if (!receiver->has_component) {
printer->Print("other-receivers\n");
return true;
}
}
return false;
});
FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
if (auto service = ElementCast<Service>(el)) {
if (!service->has_component) {
printer->Print("other-services\n");
return true;
}
}
return false;
});
// Print the supported screens
SupportsScreen* screen = ElementCast<SupportsScreen>(FindElement(root.get(),
[&](ManifestExtractor::Element* el) -> bool {
return ElementCast<SupportsScreen>(el) != nullptr;
}));
if (screen) {
screen->PrintScreens(printer, target_sdk_);
} else {
// Print the default supported screens
SupportsScreen default_screens;
default_screens.PrintScreens(printer, target_sdk_);
if (other_activities_) {
printer->Print("other-activities\n");
}
if (other_receivers_) {
printer->Print("other-receivers\n");
}
if (other_services_) {
printer->Print("other-services\n");
}
supports_screen_->PrintScreens(printer, target_sdk_);
// Print all the unique locales of the apk
printer->Print("locales:");
@@ -2320,75 +2442,7 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
}
printer->Print("\n");
// Print the supported architectures of the app
std::set<std::string> architectures;
auto it = apk_->GetFileCollection()->Iterator();
while (it->HasNext()) {
auto file_path = it->Next()->GetSource().path;
size_t pos = file_path.find("lib/");
if (pos != std::string::npos) {
file_path = file_path.substr(pos + 4);
pos = file_path.find('/');
if (pos != std::string::npos) {
file_path = file_path.substr(0, pos);
}
architectures.insert(file_path);
}
}
// Determine if the application has multiArch supports
auto has_multi_arch = FindElement(root.get(), [&](ManifestExtractor::Element* el) -> bool {
if (auto application = ElementCast<Application>(el)) {
return application->has_multi_arch;
}
return false;
});
bool output_alt_native_code = 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
if (has_multi_arch) {
// 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.
const std::string kIntel64 = "x86_64";
const std::string kArm64 = "arm64-v8a";
auto arch = architectures.find(kIntel64);
if (arch == architectures.end()) {
arch = architectures.find(kArm64);
}
if (arch != architectures.end()) {
printer->Print(StringPrintf("native-code: '%s'\n", arch->data()));
architectures.erase(arch);
output_alt_native_code = true;
}
}
if (architectures.size() > 0) {
if (output_alt_native_code) {
printer->Print("alt-");
}
printer->Print("native-code:");
for (auto& arch : architectures) {
printer->Print(StringPrintf(" '%s'", arch.data()));
}
printer->Print("\n");
}
architectures_->Print(printer);
return true;
}
@@ -2531,7 +2585,10 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Visit(xml::Elemen
int DumpManifest(LoadedApk* apk, DumpManifestOptions& options, text::Printer* printer,
IDiagnostics* diag) {
ManifestExtractor extractor(apk, options);
return extractor.Dump(printer, diag) ? 0 : 1;
if (!extractor.Extract(diag)) {
return 0;
}
return extractor.Dump(printer) ? 0 : 1;
}
} // namespace aapt