blob: cee0cd52a546c72915b663a1a71cbc03fbada237 [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
Joe Onorato70edfa82018-12-14 15:46:27 -0800162 Command cmd("aapt2");
Joe Onorato0578cbc2016-10-19 17:03:06 -0700163 cmd.AddArg("dump");
164 cmd.AddArg("xmltree");
165 cmd.AddArg(filename);
Joe Onorato70edfa82018-12-14 15:46:27 -0800166 cmd.AddArg("--file");
Joe Onorato0578cbc2016-10-19 17:03:06 -0700167 cmd.AddArg("AndroidManifest.xml");
168
169 int err;
170
171 string output = get_command_output(cmd, &err, false);
172 check_error(err);
173
174 // Parse the manifest xml
175 Scope* scope = new Scope(NULL, -1);
176 Element* root = NULL;
177 Element* current = NULL;
178 vector<string> lines;
179 split_lines(&lines, output);
180 for (size_t i=0; i<lines.size(); i++) {
181 const string& line = lines[i];
182 smatch match;
183 if (regex_match(line, match, NS_REGEX)) {
184 int depth = match[1].length() / 2;
185 while (depth < scope->depth) {
186 Scope* tmp = scope;
187 scope = scope->parent;
188 delete tmp;
189 }
190 scope = new Scope(scope, depth);
191 scope->namespaces[match[2]] = match[3];
192 } else if (regex_match(line, match, ELEMENT_REGEX)) {
193 Element* element = new Element();
194
195 string str = match[2];
196 size_t colon = str.find(':');
197 if (colon == string::npos) {
198 element->name = str;
199 } else {
200 element->ns = scope->namespaces[string(str, 0, colon)];
201 element->name.assign(str, colon+1, string::npos);
202 }
203 element->lineno = atoi(match[3].str().c_str());
204 element->depth = match[1].length() / 2;
205
206 if (root == NULL) {
207 current = element;
208 root = element;
209 } else {
210 while (element->depth <= current->depth && current->parent != NULL) {
211 current = current->parent;
212 }
213 element->parent = current;
214 current->children.push_back(element);
215 current = element;
216 }
217 } else if (regex_match(line, match, ATTR_REGEX)) {
218 if (current != NULL) {
219 Attribute attr;
220 string str = match[2];
Joe Onorato70edfa82018-12-14 15:46:27 -0800221 size_t colon = str.rfind(':');
Joe Onorato0578cbc2016-10-19 17:03:06 -0700222 if (colon == string::npos) {
223 attr.name = str;
224 } else {
Joe Onorato70edfa82018-12-14 15:46:27 -0800225 attr.ns.assign(str, 0, colon);
Joe Onorato0578cbc2016-10-19 17:03:06 -0700226 attr.name.assign(str, colon+1, string::npos);
227 }
228 attr.value = match[3];
229 current->attributes.push_back(attr);
230 }
231 }
232 }
233 while (scope != NULL) {
234 Scope* tmp = scope;
235 scope = scope->parent;
236 delete tmp;
237 }
238
239 // Package name
240 apk->package = root->GetAttr("", "package");
241 if (apk->package.size() == 0) {
242 print_error("%s:%d: Manifest root element doesn't contain a package attribute",
243 filename.c_str(), root->lineno);
244 delete root;
245 return 1;
246 }
247
248 // Instrumentation runner
249 vector<Element*> instrumentation;
250 root->FindElements("", "instrumentation", &instrumentation, true);
251 if (instrumentation.size() > 0) {
252 // TODO: How could we deal with multiple instrumentation tags?
253 // We'll just pick the first one.
254 apk->runner = instrumentation[0]->GetAttr(ANDROID_NS, "name");
255 }
256
257 // Activities
258 vector<Element*> activities;
259 root->FindElements("", "activity", &activities, true);
260 for (size_t i=0; i<activities.size(); i++) {
261 string name = activities[i]->GetAttr(ANDROID_NS, "name");
262 if (name.size() == 0) {
263 continue;
264 }
265 apk->activities.push_back(full_class_name(apk->package, name));
266 }
267
268 delete root;
269 return 0;
270}
271