123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- /*
- * 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 <regex>
- 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<N; i++) {
- if (activities[i] == fullClassName) {
- return true;
- }
- }
- return false;
- }
- struct Attribute {
- string ns;
- string name;
- string value;
- };
- struct Element {
- Element* parent;
- string ns;
- string name;
- int lineno;
- vector<Attribute> attributes;
- vector<Element*> 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<Element*>* result, bool recurse);
-
- };
- Element::Element()
- {
- }
- Element::~Element()
- {
- const size_t N = children.size();
- for (size_t i=0; i<N; i++) {
- delete children[i];
- }
- }
- string
- Element::GetAttr(const string& ns, const string& name) const
- {
- const size_t N = attributes.size();
- for (size_t i=0; i<N; i++) {
- const Attribute& attr = attributes[i];
- if (attr.ns == ns && attr.name == name) {
- return attr.value;
- }
- }
- return string();
- }
- void
- Element::FindElements(const string& ns, const string& name, vector<Element*>* result, bool recurse)
- {
- const size_t N = children.size();
- for (size_t i=0; i<N; i++) {
- Element* child = children[i];
- if (child->ns == ns && child->name == name) {
- result->push_back(child);
- }
- if (recurse) {
- child->FindElements(ns, name, result, recurse);
- }
- }
- }
- struct Scope {
- Scope* parent;
- int depth;
- map<string,string> 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<string> lines;
- split_lines(&lines, output);
- for (size_t i=0; i<lines.size(); i++) {
- const string& line = lines[i];
- smatch match;
- if (regex_match(line, match, NS_REGEX)) {
- int depth = match[1].length() / 2;
- while (depth < scope->depth) {
- 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<Element*> 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<Element*> activities;
- root->FindElements("", "activity", &activities, true);
- for (size_t i=0; i<activities.size(); i++) {
- string name = activities[i]->GetAttr(ANDROID_NS, "name");
- if (name.size() == 0) {
- continue;
- }
- apk->activities.push_back(full_class_name(apk->package, name));
- }
- delete root;
- return 0;
- }
|