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("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 
271