blob: 961b47cdfecddb9dfaa3597e5329140285e567d5 [file] [log] [blame]
Joe Onorato0578cbc2016-10-19 17:03:06 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "aapt.h"
18
19#include "command.h"
20#include "print.h"
21#include "util.h"
22
23#include <regex>
24
25const regex NS_REGEX("( *)N: ([^=]+)=(.*)");
26const regex ELEMENT_REGEX("( *)E: ([^ ]+) \\(line=(\\d+)\\)");
27const regex ATTR_REGEX("( *)A: ([^\\(=]+)[^=]*=\"([^\"]+)\".*");
28
29const string ANDROID_NS("http://schemas.android.com/apk/res/android");
30
31bool
32Apk::HasActivity(const string& className)
33{
34 string fullClassName = full_class_name(package, className);
35 const size_t N = activities.size();
36 for (size_t i=0; i<N; i++) {
37 if (activities[i] == fullClassName) {
38 return true;
39 }
40 }
41 return false;
42}
43
44struct Attribute {
45 string ns;
46 string name;
47 string value;
48};
49
50struct Element {
51 Element* parent;
52 string ns;
53 string name;
54 int lineno;
55 vector<Attribute> attributes;
56 vector<Element*> children;
57
58 /**
59 * Indentation in the xmltree dump. Might not be equal to the distance
60 * from the root because namespace rows (scopes) have their own indentation.
61 */
62 int depth;
63
64 Element();
65 ~Element();
66
67 string GetAttr(const string& ns, const string& name) const;
68 void FindElements(const string& ns, const string& name, vector<Element*>* result, bool recurse);
69
70};
71
72Element::Element()
73{
74}
75
76Element::~Element()
77{
78 const size_t N = children.size();
79 for (size_t i=0; i<N; i++) {
80 delete children[i];
81 }
82}
83
84string
85Element::GetAttr(const string& ns, const string& name) const
86{
87 const size_t N = attributes.size();
88 for (size_t i=0; i<N; i++) {
89 const Attribute& attr = attributes[i];
90 if (attr.ns == ns && attr.name == name) {
91 return attr.value;
92 }
93 }
94 return string();
95}
96
97void
98Element::FindElements(const string& ns, const string& name, vector<Element*>* result, bool recurse)
99{
100 const size_t N = children.size();
101 for (size_t i=0; i<N; i++) {
102 Element* child = children[i];
103 if (child->ns == ns && child->name == name) {
104 result->push_back(child);
105 }
106 if (recurse) {
107 child->FindElements(ns, name, result, recurse);
108 }
109 }
110}
111
112struct Scope {
113 Scope* parent;
114 int depth;
115 map<string,string> namespaces;
116
117 Scope(Scope* parent, int depth);
118};
119
120Scope::Scope(Scope* p, int d)
121 :parent(p),
122 depth(d)
123{
124 if (p != NULL) {
125 namespaces = p->namespaces;
126 }
127}
128
129
130string
131full_class_name(const string& packageName, const string& className)
132{
133 if (className.length() == 0) {
134 return "";
135 }
136 if (className[0] == '.') {
137 return packageName + className;
138 }
139 if (className.find('.') == string::npos) {
140 return packageName + "." + className;
141 }
142 return className;
143}
144
145string
146pretty_component_name(const string& packageName, const string& className)
147{
148 if (starts_with(packageName, className)) {
149 size_t pn = packageName.length();
150 size_t cn = className.length();
151 if (cn > pn && className[pn] == '.') {
152 return packageName + "/" + string(className, pn, string::npos);
153 }
154 }
155 return packageName + "/" + className;
156}
157
158int
159inspect_apk(Apk* apk, const string& filename)
160{
161 // Load the manifest xml
162 Command cmd("aapt");
163 cmd.AddArg("dump");
164 cmd.AddArg("xmltree");
165 cmd.AddArg(filename);
166 cmd.AddArg("AndroidManifest.xml");
167
168 int err;
169
170 string output = get_command_output(cmd, &err, false);
171 check_error(err);
172
173 // Parse the manifest xml
174 Scope* scope = new Scope(NULL, -1);
175 Element* root = NULL;
176 Element* current = NULL;
177 vector<string> lines;
178 split_lines(&lines, output);
179 for (size_t i=0; i<lines.size(); i++) {
180 const string& line = lines[i];
181 smatch match;
182 if (regex_match(line, match, NS_REGEX)) {
183 int depth = match[1].length() / 2;
184 while (depth < scope->depth) {
185 Scope* tmp = scope;
186 scope = scope->parent;
187 delete tmp;
188 }
189 scope = new Scope(scope, depth);
190 scope->namespaces[match[2]] = match[3];
191 } else if (regex_match(line, match, ELEMENT_REGEX)) {
192 Element* element = new Element();
193
194 string str = match[2];
195 size_t colon = str.find(':');
196 if (colon == string::npos) {
197 element->name = str;
198 } else {
199 element->ns = scope->namespaces[string(str, 0, colon)];
200 element->name.assign(str, colon+1, string::npos);
201 }
202 element->lineno = atoi(match[3].str().c_str());
203 element->depth = match[1].length() / 2;
204
205 if (root == NULL) {
206 current = element;
207 root = element;
208 } else {
209 while (element->depth <= current->depth && current->parent != NULL) {
210 current = current->parent;
211 }
212 element->parent = current;
213 current->children.push_back(element);
214 current = element;
215 }
216 } else if (regex_match(line, match, ATTR_REGEX)) {
217 if (current != NULL) {
218 Attribute attr;
219 string str = match[2];
220 size_t colon = str.find(':');
221 if (colon == string::npos) {
222 attr.name = str;
223 } else {
224 attr.ns = scope->namespaces[string(str, 0, colon)];
225 attr.name.assign(str, colon+1, string::npos);
226 }
227 attr.value = match[3];
228 current->attributes.push_back(attr);
229 }
230 }
231 }
232 while (scope != NULL) {
233 Scope* tmp = scope;
234 scope = scope->parent;
235 delete tmp;
236 }
237
238 // Package name
239 apk->package = root->GetAttr("", "package");
240 if (apk->package.size() == 0) {
241 print_error("%s:%d: Manifest root element doesn't contain a package attribute",
242 filename.c_str(), root->lineno);
243 delete root;
244 return 1;
245 }
246
247 // Instrumentation runner
248 vector<Element*> instrumentation;
249 root->FindElements("", "instrumentation", &instrumentation, true);
250 if (instrumentation.size() > 0) {
251 // TODO: How could we deal with multiple instrumentation tags?
252 // We'll just pick the first one.
253 apk->runner = instrumentation[0]->GetAttr(ANDROID_NS, "name");
254 }
255
256 // Activities
257 vector<Element*> activities;
258 root->FindElements("", "activity", &activities, true);
259 for (size_t i=0; i<activities.size(); i++) {
260 string name = activities[i]->GetAttr(ANDROID_NS, "name");
261 if (name.size() == 0) {
262 continue;
263 }
264 apk->activities.push_back(full_class_name(apk->package, name));
265 }
266
267 delete root;
268 return 0;
269}
270