1 /*
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 
25 const regex NS_REGEX("( *)N: ([^=]+)=(.*)");
26 const regex ELEMENT_REGEX("( *)E: ([^ ]+) \\(line=(\\d+)\\)");
27 const regex ATTR_REGEX("( *)A: ([^\\(=]+)[^=]*=\"([^\"]+)\".*");
28 
29 const string ANDROID_NS("http://schemas.android.com/apk/res/android");
30 
31 bool
HasActivity(const string & className)32 Apk::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 
44 struct Attribute {
45     string ns;
46     string name;
47     string value;
48 };
49 
50 struct 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 
Element()72 Element::Element()
73 {
74 }
75 
~Element()76 Element::~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 
84 string
GetAttr(const string & ns,const string & name) const85 Element::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 
97 void
FindElements(const string & ns,const string & name,vector<Element * > * result,bool recurse)98 Element::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 
112 struct Scope {
113     Scope* parent;
114     int depth;
115     map<string,string> namespaces;
116 
117     Scope(Scope* parent, int depth);
118 };
119 
Scope(Scope * p,int d)120 Scope::Scope(Scope* p, int d)
121     :parent(p),
122      depth(d)
123 {
124      if (p != NULL) {
125          namespaces = p->namespaces;
126      }
127 }
128 
129 
130 string
full_class_name(const string & packageName,const string & className)131 full_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 
145 string
pretty_component_name(const string & packageName,const string & className)146 pretty_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 
158 int
inspect_apk(Apk * apk,const string & filename)159 inspect_apk(Apk* apk, const string& filename)
160 {
161     // Load the manifest xml
162     Command cmd("aapt2");
163     cmd.AddArg("dump");
164     cmd.AddArg("xmltree");
165     cmd.AddArg(filename);
166     cmd.AddArg("--file");
167     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];
221                 size_t colon = str.rfind(':');
222                 if (colon == string::npos) {
223                     attr.name = str;
224                 } else {
225                     attr.ns.assign(str, 0, colon);
226                     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 
272