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