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