/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "aapt.h" #include "command.h" #include "print.h" #include "util.h" #include const regex NS_REGEX("( *)N: ([^=]+)=(.*)"); const regex ELEMENT_REGEX("( *)E: ([^ ]+) \\(line=(\\d+)\\)"); const regex ATTR_REGEX("( *)A: ([^\\(=]+)[^=]*=\"([^\"]+)\".*"); const string ANDROID_NS("http://schemas.android.com/apk/res/android"); bool Apk::HasActivity(const string& className) { string fullClassName = full_class_name(package, className); const size_t N = activities.size(); for (size_t i=0; i attributes; vector children; /** * Indentation in the xmltree dump. Might not be equal to the distance * from the root because namespace rows (scopes) have their own indentation. */ int depth; Element(); ~Element(); string GetAttr(const string& ns, const string& name) const; void FindElements(const string& ns, const string& name, vector* result, bool recurse); }; Element::Element() { } Element::~Element() { const size_t N = children.size(); for (size_t i=0; i* result, bool recurse) { const size_t N = children.size(); for (size_t i=0; ins == ns && child->name == name) { result->push_back(child); } if (recurse) { child->FindElements(ns, name, result, recurse); } } } struct Scope { Scope* parent; int depth; map namespaces; Scope(Scope* parent, int depth); }; Scope::Scope(Scope* p, int d) :parent(p), depth(d) { if (p != NULL) { namespaces = p->namespaces; } } string full_class_name(const string& packageName, const string& className) { if (className.length() == 0) { return ""; } if (className[0] == '.') { return packageName + className; } if (className.find('.') == string::npos) { return packageName + "." + className; } return className; } string pretty_component_name(const string& packageName, const string& className) { if (starts_with(packageName, className)) { size_t pn = packageName.length(); size_t cn = className.length(); if (cn > pn && className[pn] == '.') { return packageName + "/" + string(className, pn, string::npos); } } return packageName + "/" + className; } int inspect_apk(Apk* apk, const string& filename) { // Load the manifest xml Command cmd("aapt2"); cmd.AddArg("dump"); cmd.AddArg("xmltree"); cmd.AddArg(filename); cmd.AddArg("--file"); cmd.AddArg("AndroidManifest.xml"); int err; string output = get_command_output(cmd, &err, false); check_error(err); // Parse the manifest xml Scope* scope = new Scope(NULL, -1); Element* root = NULL; Element* current = NULL; vector lines; split_lines(&lines, output); for (size_t i=0; idepth) { Scope* tmp = scope; scope = scope->parent; delete tmp; } scope = new Scope(scope, depth); scope->namespaces[match[2]] = match[3]; } else if (regex_match(line, match, ELEMENT_REGEX)) { Element* element = new Element(); string str = match[2]; size_t colon = str.find(':'); if (colon == string::npos) { element->name = str; } else { element->ns = scope->namespaces[string(str, 0, colon)]; element->name.assign(str, colon+1, string::npos); } element->lineno = atoi(match[3].str().c_str()); element->depth = match[1].length() / 2; if (root == NULL) { current = element; root = element; } else { while (element->depth <= current->depth && current->parent != NULL) { current = current->parent; } element->parent = current; current->children.push_back(element); current = element; } } else if (regex_match(line, match, ATTR_REGEX)) { if (current != NULL) { Attribute attr; string str = match[2]; size_t colon = str.rfind(':'); if (colon == string::npos) { attr.name = str; } else { attr.ns.assign(str, 0, colon); attr.name.assign(str, colon+1, string::npos); } attr.value = match[3]; current->attributes.push_back(attr); } } } while (scope != NULL) { Scope* tmp = scope; scope = scope->parent; delete tmp; } // Package name apk->package = root->GetAttr("", "package"); if (apk->package.size() == 0) { print_error("%s:%d: Manifest root element doesn't contain a package attribute", filename.c_str(), root->lineno); delete root; return 1; } // Instrumentation runner vector instrumentation; root->FindElements("", "instrumentation", &instrumentation, true); if (instrumentation.size() > 0) { // TODO: How could we deal with multiple instrumentation tags? // We'll just pick the first one. apk->runner = instrumentation[0]->GetAttr(ANDROID_NS, "name"); } // Activities vector activities; root->FindElements("", "activity", &activities, true); for (size_t i=0; iGetAttr(ANDROID_NS, "name"); if (name.size() == 0) { continue; } apk->activities.push_back(full_class_name(apk->package, name)); } delete root; return 0; }